Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance Items #42

Open
HimeWorks opened this issue Dec 3, 2013 · 47 comments
Open

Instance Items #42

HimeWorks opened this issue Dec 3, 2013 · 47 comments
Assignees
Labels

Comments

@HimeWorks
Copy link
Member

@HimeWorks HimeWorks commented Dec 3, 2013

Background

The default project stores the database as a series of arrays, indexed by ID.
All data within the game reference database entries with this ID, as you would with any other database system.

We can extend the database to support instance items by simply inserting a new record when necessary.

Concepts

This implementation introduces two concepts

  • Template items
  • Instance items

Template Items

"Template items" are exactly what the name suggests: they are the objects that you create through the database editor. For example, you can create a Short Sword as weapon 1 in the database.

The range of ID's reserved for template items go from 1 to MAX_TABLE_SIZE, so for example if the size of the weapons array was 100, then everything from 1 to 100 are considered template items.

Instance Items

"Instance items" are specific instances of an item. They are created from template items, and are stored as a unique item. Instance items can have the exact same properties as a template item, but the difference is that you can modify an instance and not affect any other instance.

The purpose of instance items is to allow you to have multiple instances of the same template item, each with their own unique properties (parameters, features, names, etc.)

Working with Instances

Throughout the project, you will be required to handle both template items and instance items appropriately, depending on the situation.

Creating Instances

Creating an instance item is very easy: when you gain a template item, pass that through an instance creator, which will insert a new entry in the database and return that ID instead.

Here is a simple workflow. Assume we have 20 items in our database initially.

  1. "Gain Item" event is called, which adds Item 3 to the party's inventory
  2. The gain_item method recognizes that ID 3 is a template item, and will proceed to create an instance of that item.
  3. The instance is assigned an ID of 21 (the first unused instance ID), and returns that to the original gain_item method, where item 21 is added to the inventory instead.

Retrieving Instances

The gain_item method is called EVERYTIME the party needs to gain an item. The example above showed that an event command was called to add an item to the party, which uses template items since they were created in the database.

More dynamic functionality such as changing your equips work with instance items instead. Suppose we have 60 template weapons in the database.

  1. "change equip" function occurs, where you replace your Short Sword (ID 101) with a Long Sword (ID 120)
  2. The gain_item method recognizes that ID 101 is an instance item, and does not create a new instance of the item
  3. Equips are swapped as usual.

Now what happens if you are using the "change equip" event command to forcefully equip something?

  1. "change equip" event command is called, replacing Short Sword (ID 101) with a Long Sword (ID 2)
  2. The change equip by ID method recognizes that ID 2 is a template item, and proceeds to create an instance of it before calling the change_equip method for the actor.
  3. Equips are swapped as usual.

Essentially, all that is required is to find all of the different methods that are exposed to the interpreter and perform the appropriate checks to make sure that whatever objects go into the game objects are instance items.

Counting Instances

All instances will store their template ID's. Even though you have three separate short sword instances, they are still short swords, and if you ask how many short swords the party has, it should say 3.

When you do a count using a template, the count will go through all of the instances and find the ones that have a matching template ID.

Updating Instances

Updating instances in the database may be required if you want to modify an instance item. For example, if you upgrade it or enchant it or you damage it and its durability goes down.

Since all items in your inventory should be instance items already, it is just a matter of updating the fields as you normally would with any other object.

Deleting Instances

There are no plans to support the deletion of instance items from the database. This is because there is no reliable with to keep track of whether an instance is currently in use or not (since it is referenced by ID, and ANY arbitrary object could be referencing it)

This problem is partially due to the fact that everything is database-oriented. If instead of using a database, we simply store instances as self-contained objects, then when an instance is deleted, it is deleted for good and we don't need to consume any space for it.

However, because the default project heavily relies on database indexing, and to improve compatibility I would like to remain consistent with the design, we're stuck with a lot of possibly unused database entries.

Saving instances

An instance item only makes sense in the current game, and so they should be stored somewhere game-specific. The easiest way is to store this in the save file.

How the instance items should be tracked is an implementation detail, but the way I would do it is to keep track of an array of instance items, and combine that with the database at run-time. This allows you to transparently manage all instance items as if they were regular items in the database without doing any special checks.

When you save the game, the instance data will be written out.
Upon loading the game, you would simply combine the instance data with the template data to form the original database.

Savefile Bloat

Storing instance data is not cheap, especially when you have hundreds of thousands of instances. There may be ways to optimize this, but for the most part, players may end up with massive save files.

@ghost ghost assigned HimeWorks Dec 3, 2013
@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 3, 2013

I am not sure I follow you exactly as described... So I hope I am not frustrating you with my simplification. :)

Say that I have a script that is supposed to check if the party has a number of runes that are instance armor templates. Normally I would call:

#Code may not be exact
has_rune1 = $game_party.item_number($data_armor[10])  > 0
has_rune2 = $game_party.item_number($data_armor[11])  > 0
has_rune3 = $game_party.item_number($data_armor[12])  > 0

do_something if has_rune1 && has_rune2 && has_rune3 

Does that change at all using your instance items?

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 3, 2013

It shouldn't. I have updated the info to include this.

For example, you have your Short Sword template, followed by 3 short sword instances.
When you ask "how many short swords does the party have?", you would probably expect 3 right?

Each instance item would hold a reference to its template ID. Determining how many short swords you have would be going through ALL your weapon instances and checking whether they are short swords or not.

Which CAN be terribly inefficient if you have a lot of instances (say, 1000 weapons. Then you'd have to search through 1000 instances).

I haven't thought of a way to make this more efficient though without changing how the inventory is stored, but maybe it would be appropriate to change the way inventory is stored. For example, the current inventory is like this

items = {
   1 => 10,
   2 => 15
}

Which means you have 10 of item 1, and 15 of item 2.
This is a pretty silly way to store your items if you're working with instance items, because it'll just be

items = {
    1 => 1,
    2 => 1,
    3 => 1,
    ...
}

Which is kind of pointless.

There are some benefits to storing it as an array of instance items though. For example, you can keep track of item order, so that easily lends itself to "manual sorting" in your inventory. It also allows you to leave gaps in your inventory.

Another option is to explicitly keep track of item count separately, and make sure that whenever any sort of inventory transaction occurs, you update that counter. This is prone to error, but if done carefully, it would be much more efficient than counting it up everytime you need the numbers.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 10, 2013

Here is a solution to the counting problem. Here's how it looks

# same as before. Just keeping count of your stuff
@weapons = {1 => 5}
@items = {1 => 5}
@armors = { 1 => 5 }

# New arrays containing the objects themselves
@weapon_list = [weapon1, weapon2, weapon3, ... ]
@item_list = [item1, item2, item3, ... ]
@armor_list = [armor1, armor2, armor3, ... ]

When you take an item from your inventory, the "inventory manager" will record this by subtracting one from the template ID's total. For example if you initially start with 5 potions, and you want to take out a potion for some reason, it would first deduct the potion count by 1 before allowing you to take it out.

When you put an item into the inventory, a similar process occurs: the inventory manager records this by first adding one to the template ID's total. It goes one step further: it checks the ID of the item: if it is a template item, it goes and wraps it up into an instance item before putting it into the inventory. If it is already an instance item, it would just go into the inventory.

This allows you to easily keep track of how many potions you have, while allowing you to randomly throw your instances anywhere in your inventory.

Because you must always go through gain_item and lose_item in order to put an item in or take an item out, the counts are always correct.

This implementation of instance items is quite flexible and leads to a fairly flexible inventory system that can be expanded on. It does not require any sophisticated way of storing the items, and you keep easily keep track of your inventory simply because whatever goes in or out must go through a single gatekeeper.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 10, 2013

I've finished writing the script. Since you use dozens of scripts in your project it would probably be a good place to test this instance item script.

I will not be releasing it until I have at least one or two equips related scripts finished. I'll probably start with equip levels, followed by an affix system.

The instance items implements all of the things I've discussed above, and maybe some extras that I decided during implementation. Read the documentation inside the script for more details.

In particular, I have added something called instance mode. This is basically to allow you to choose whether you want certain classes of objects to allow instances or not. For example, maybe you only want weapons to be treated as instances in your project, or you don't care about items as instances.

You can get a copy of the script here. I've tested it for the most part.
https://www.dropbox.com/sh/sz6mpw5n2d6zxi2/uaT63tPvGw/RMVXA%20Scripts/Instance_Items.txt

I believe it is possible to make it so that you can specify whether a specific object supports instances or not, but I have not figured out how that would work into this design.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 10, 2013

Preliminary testing suggests that there is some issue with Equip Slots Core. I had to place this script above it to stop this error.
instance_items_issue

Also, my Equip Events (unequip) events don't seem to fire correctly regardless of the scripts location in the list. Dynamic slots are adding, but not removing themselves when I unequip the test item. After a little while the Game_Interpreter errored, line 1411.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 10, 2013

I've added compatibility patch for the equip slots. See if the equip events problem is resolved.
Interpreter Line 1411 doesn't mean anything. It's just the "eval" call. You need to use the script call traceback script to see the real error.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 10, 2013

Sorry for the delay.

The Equip Slots error is gone. No matter where the script is in the list. Which means I can have it below the Equip Slots and Dynamic Equip Slots.

However, I am now getting the line 1411 error no matter what, when removing a slot via unequip.

I would do more testing but I've been up since 2am, its 5pm now, and my brain is in slow motion. :p

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 11, 2013

You should place instance items underneath equip slots.

The script call you'll have to have http://himeworks.wordpress.com/2013/08/04/script-call-traceback/ included.
I have not looked into dynamic equips.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 11, 2013

Last time I tried to use the trace back it didn't work. Never got the output on error.

I'll try it out again when I get to working.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 11, 2013

Yeah, something is clashing the moment I start up the test... So trace back isn't going to work.

script_call_traceback

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 11, 2013

Are you sure that's not what one of your events or common events are doing?
Cause that error message is what the script call traceback is telling you, which means it caught something from the game interpreter.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 11, 2013

I don't know what it could be, seeing as I have no trouble with any errors before adding that script.

No auto run, or parallel events, map or common. I have no clue why its happening. :/

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 11, 2013

I'll have to see a demo.
It might have uncovered a bug with another script.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 11, 2013

I'll see what I can do. :)

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 12, 2013

Go figure.

This is the third time that I honestly thought I had not caused the problem, only to determine that I did.

Looks like I had forgotten to add your Full Error Backtrace script to my 'script load'... Which is why the Script Call Traceback wasn't working.

All good now.

Here is the error output from the instance item issue with dynamic slot removal via equipment change.

instance_items_issue_2

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Dec 12, 2013

I'm assuming this only occurs when the Instance Items script is added.
The error is strange, as it seems to suggest that you are actually trying to access object from your script call. Determine which event is running, screenshot that and post it.

I'm assuming script call traceback is inserted properly above Main.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 14, 2013

Yes, everything was correct on your end.

I took a day to focus on something else, as that tends to help me get out of any tunnel vision I may develop after not being able to see something that should be obvious.

Turns out that the issue was not the instance items, but an issue I created earlier without noticing, regarding very large slot ID's and my custom modular script method overwrite of one of your methods. And since I am no longer using that particular work around, due to scrapping the default load-out mechanic for modular items, I just commented out that overwrite, and everything seems to be running smoothly again.

The closer I get to actually sitting down and pumping out a playable game, the more mistakes I seem to be making. :p

Learning a new lesson every time I turn around.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Dec 19, 2013

As of right now I have removed this script from my list because its superfluous. I think I have planned a rather complex system of class/skill/item use and adding yet another layer of complexity will only delay my first chapter even longer.

So I am forcing myself to get something done, ASAP. Which is why I have been rather uninvolved here lately. :)

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 7, 2014

Since Selchar on RMW has written 4 scripts for my instance items script I have decided to release it.
http://himeworks.wordpress.com/2014/01/07/instance-items/

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 11, 2014

I have made a pretty large shift in my project direction this weekend, and it turns out I will have use for Instanced Items after all... So I will likely be sharing what I come up with soon.

Note: Is there a way to easily link a skill to a piece of equipment so that you know that when 'X' skill was used, 'Y' item was its source? Any ideas how I might accomplish that? I will be attempting to make that work within the next few days, or late this coming week.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 11, 2014

That really depends on what you want to achieve, as you can have skills tied to equips on a one-to-one basis, for example, Fire Storm is always linked to a Fire Rod.

On the other hand, you can have the following skill system (not something I came up with on my own. I took various elements from games like Final Fantasy Tactics, Vandal Hearts, and Disgaea)

  • Skills are attached to Equips
  • To perform an action, you select an equip, and then use a skill attached to that equip
  • Each skill can be mastered (perhaps through usage)
  • Once a skill is mastered, you can move it to another piece of equip
  • This system allows you to learn new skills through acquiring equips, and allows you to move your favourite skills to different equips.

Consider opening a new issue to describe what you are actually trying to do.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 12, 2014

Now that I updated your script to the latest version there is an issue with the instanced items all returning a '0' container count and being unusable in any way. They still show up in the item list, but I can not equip them.

I am trying to find out why...

Note: The older version I had, was working fine.

@Selchar
Copy link

@Selchar Selchar commented Jan 12, 2014

Just checked it out, I've been using the previous version so I'm still good to go, but the update that was done earlier today seems to have broke a few things.

1: As Roguedeus said, in instance mode instance equips can't be... well... equipped!
2: In Non-Instanced mode, initial equips are instances, items gained are templates but can't be equipped(but initial equip instances can be).

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 12, 2014

I think I know why. When I changed the way items were being counted, I first removed the calls to add the template item. I then removed the aliased gain_item calls to add the instance item, but I forgot to actually increase the amount of the instance item by 1.

For the non-instance mode, I didn't test that at all because it didn't occur to me that someone might actually disable instance mode for equips if they were using my script. The issue was resolved by adding some conditionals.

Both issues have been addressed.

There is a minor bug where if you explicitly add an instance item to your inventory yourself, you'll have strange duplicate references.

For example, say your Hand Ax template was ID 1, and you have 60 weapons in your database. Therefore, ID's 1 to 60 are template weapons.

$game_party.gain_item($data_weapons[1], 1)
$game_party.gain_item($data_weapons[61], 1)

The first call will realize that it is a template item, and the instance manager will generate an instance, with the ID 61 (since it's the first available spot). Then, the second call adds item 61...except now, the instance manager will realize it's an instance already, and simply add that to the inventory.

Now, you have two copies of the same instance in your inventory, like this

[ Weapon 61, Weapon 61 ]

If you try to equip one of them or remove one of them, both of them will disappear (because the engine does an array delete on the reference, which takes out both of them)

This occurs if you explicitly add instance items to your inventory yourself. I don't know where else it may occur, nor am I sure if this is a major issue.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 14, 2014

Have you considered the possibility of allowing for the disabling of instancing for individual items? And/or the reverse? An all or nothing situation, I think, might be one of the primary reasons of item bloat. I can think of a TON of armor items I wouldn't need instanced, and ironically, those will be the most numerous armor type items in the game.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 14, 2014

I can't think of a way to do it with my existing design. The instance items are stored in an array, and I simply return that array to anyone that needs a list of items.

@HimeWorks HimeWorks mentioned this issue Jan 14, 2014
@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 14, 2014

If you don't care about the details you can skip this post and read the one after which summarizes how I deal with names. This post is simply the thought-process that led up to the solution.

There are many other issues when it comes to different scripts all accessing the same set of shared variables, namely

  • name
  • params
  • features
  • price

And basically everything else, but so far those are the most obvious, and so might as well see how to deal with those.

A flexible solution is required to allow different scripts to work with one another without ANY knowledge of what the other script does. You can do anything you want with the item; I will not make any attempt to try to figure out what you did. This allows for clean code and avoids us trying to check a dozen things (like what you probably have experienced with something simple like a prefix/suffix)

I will start by addressing the name compatibility issue

Under the assumption that scripts do not need any knowledge of other scripts, that means my affix system should take a Short Sword Level 1 and simply add on a prefix without modifying the name: Mighty Short Sword Level 1

A prefix is easy, but what about a suffix? Since my script assumes Short Sword Level 1 is the name of the weapon, I would probably just go and turn it into Short Sword Level 1 of Chaos. Doesn't look right.

One solution that I'm leaning towards is the idea of re-applying all changes, and delegating that task to each script, in a certain order.

Consider how the name of a weapon that has a prefix, suffix, level, and durability is structured.
We start with Prefix, followed by the Base Name, followed by the Suffix, then the Level, and finally the Durability.
That is translated to code as follows

def refresh_name

   # the base method, defined in the instance items framework
   name = basename

   # extra stuff, from various scripts
   name = apply_prefix(name)
   name = apply_suffix(name)
   name = apply_level(name)
   name = apply_durability(name)

   # return it so more scripts can use it
   name
end

My framework will assign the final name into @name, which is returned by the name method (which we will say is part of the Item interface for getting the name of an item).

What is important here is that the order is preserved. This requires you to ask users to place scripts in a certain order, but that shouldn't be too bad if we are under the assumption that scripts do not know anything about other separate scripts (so in your case, your Exp Base script and your actual Exp script can know about each other no problem).

For performance reasons, I would like to cache the name so we're not building the name everything we need it.
However, anytime you need to refresh the name (for example, your level increases, durability goes down, prefix/suffix changes), you simply call the refresh_name method and move on.

I believe this solution should be flexible enough to apply to other shared variables without too much problem.
Let me know if you see any potential issues from any of the assumptions I've made about script transparency, script ordering, or rebuilding things from scratch.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 14, 2014

To summarize how the name compatibility issue is dealt with

  1. The framework defines a method for working with the name
  2. Your script will alias this method and apply your changes to the name
  3. Your script will return the new modified name
  4. Other scripts can then take that name and modify it themselves

Example: Dragonic Short Sword of Fire Level 1 would be built as follows:

Begin
- base name:      Short Sword
- apply prefix:   Dragonic Short Sword
- apply suffix:   Dragonic Short Sword of Fire
- apply level:    Dragonic Short Sword of Fire Level 1
End

Everytime you need to refresh the name, the framework starts with the template name and passes it through the modifiers, before storing it in the @name variable.

In general, the framework provides a set of methods that will allow you to modify shared variables, and after all of the changes have been made, the framework takes it and stores it. By default, all of the variables defined in the RPG class are shared variables.

The order in which modifications are made is important, but other than that, scripts are free to do whatever they want with the variables as long as they pass it back to the framework. This allows you to only focus on your own scripts and not have to worry about some other script coming in and giving you special cases to consider.

Of course, I haven't implemented yet, but will likely do so tomorrow for name, params, features, and price at the very least.

@Selchar
Copy link

@Selchar Selchar commented Jan 14, 2014

It sounds reasonable to me. At the moment I have everything set up so that order doesn't matter at all, with the exceptions of course being all must come after your Instance Items script, and my exp leveling comes after my equip leveling base, and 1 very small compatibility I mad for your Affix System.

Names go like this. My durability script runs a check for my leveling base, if detected it uses a method I placed in there, if not, it checks for your affix items, and adds the prefix/suffix, if neither check returns true, it just adds on the "durability suffix"

Leveling Base runs a check for affix items first, to build a "base" for the name, applies affix params, applies params from my stat variance script, does level related stuff, adds on the "level suffix", then finally runs a check for the durability script and adds on the "durability suffix" if detected.

I'm already pretty much doing what you're suggesting except anyone trying to follow the logic would just end up confused. It doesn't care about order, but the complexity that allowed that...

In conclusion you have my full support. I'm ok with readjusting to something more, shall we say, linear if it means I don't have to jump around everywhere to figure out how to include another scripts name/param/price additions.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 14, 2014

The reason why I encourage you to assume your scripts know nothing about each other is because at some point, you may end up putting a dozen different conditional checks if you want your script to be compatible with other scripts that also change shared variables if there is no standardized way to do it.

I believe things like if $imported[:something] is a terrible way to ensure compatibility. It works, but it's not a scalable solution. If you can't do a clean alias without running into problems, then the other script is likely doing something bad.

The math, however, may become a little difficult, simply because of properties like distribution and commutativity.
Suppose we have this kind of formula for a skill

(base + level_bonus) * durability_modifier

Which means that the more worn out your weapon is, the lower the damage.
This is not the same as saying

(base * durability_modifier) + level_bonus

I am currently not TOO sure if enforcing script order will lead to a problem where you simply cannot express your mathematical formulas correctly, so I'll just go with the proposed solution and see how it goes from there.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 14, 2014

I have introduced the concept of refreshing data.

This allows you to have the engine grab the template data and apply all of the changes in some particular order (currently based on alias order, which is your script order), and then storing the final values.

Specifically for the name, the following method is available

def make_name(name)
   return name
end

The framework passes in the template name.
You can alias that and modify the name, but make sure that you return your new name.

You should never call make_name yourself. Instead, if you need to refresh the object's data for some reason (item level increased, durability went down, etc.), use the refresh_name method, or the more general refresh method which will refresh everything that is supported.

Currently, only name and params are supported.

Refer to my item affixes to see how it could be used.
Note the importance of method call order: you should always call the alias first before applying your modifiers.

@Selchar
Copy link

@Selchar Selchar commented Jan 14, 2014

Works like a charm! Once there is a refresh_price method for items/equips, I can remove the last $imported checks in my leveling base and durability scripts. Didn't realize until now how messy they became while I was working out how to get things working properly with those checks.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 16, 2014

I have not attempted to decypher your code yet, but from what I have read here, so far, seems to indicate that when the game attempts to grab an item instance, it simply grabs the first instance of the template that its looking for...

This makes me think that they are also added to inventory in the order they are instanced? Such as when you open your inventory, the items are listed in the order they were looted?

I see, from using the script in game, that its not hard to select a specific instance to equip. This allows a specific item with specific stats to be selected over another... But, I am curious what I would need to do in order to tell an external script, that normally just grabs an item container with a count value, that I instead want to grab a specific instance of the items in question.

Or, is it at all possible to intercept that action, and pop up a list of that templates instances?

Example:
Bubbles Crafting... If I wanted to use a specific instance, in a crafting action, how would I go about allowing the player to select between the possible instances?

Ditto with the Dismantle Script.

Essentially what I am trying to do is allow the player to 'repair' a specific instance via 'crafting' with it. Also, dismantle a specific instance, if its no longer wanted. Right now, I assume attempting to do either, will simply grab the first template of either item it sees. Where as I will need the player to be able to choose which.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 16, 2014

You'll have to modify the code that grabs an item by inserting some logic to open a item-list window, and then when the player selects an item, notify the scene that a selection was made and process that.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 16, 2014

Yeah, but my question is how. I am not familiar with any of it.

I am looking over your Instance Items script. But it looks like I'll need to just call the default item list from something like Scene_Equip or Scene_Item? Etc...

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 16, 2014

Yes, it has basically nothing to do with this script. This script simply provides the functionality for correctly and transparently handling items. This is why those two scenes can correctly handle items even though I didn't make any changes to them.

Figure out the logic behind how one of those scenes works and how they are able to have the player select an item.

I've added support for price and features for the refresh scheme. The following methods are available

refresh_price
make_price

refresh_features
make_features
@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 16, 2014

Ok, this is harder then I expected...

Window_ItemList derived class...
using include? method
With x,y & width,height defined by the scene space its filling.

Taking this into another topic.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 26, 2014

Selchar, small bug in the Game_Actor > equippable? method when the script is used with Yanfly's Shop Options. (Not sure about anything else) Here is my solution for your end. Also added it to my Equips Durability script. I check it for being EquipItem as those are the only ones I assign durability to. I'm sure you'll know what else is needed if its needed.

If this was addressed already in one of your newer versions just ignore me. :)

#===============================================================================
# Can't equip if weapon uses durability and has none left.
#===============================================================================
class Game_Actor < Game_Battler
  alias :zero_durability_wep_equip :equippable?
  def equippable?(item)
    return zero_durability_wep_equip(item) if item.is_a?(RPG::EquipItem) && item.is_template? #Will error if we attempt to check ZERO if template.
    return false if item.is_a?(RPG::Weapon) && item.use_durability && item.durability.zero?
    return zero_durability_wep_equip(item)
  end
end
@Selchar
Copy link

@Selchar Selchar commented Jan 26, 2014

Probably should have put that in your equipment skills thread here, or sent me a message somehow as it's not related to "instance items" in general, but my script which is used by yours... I can't think of why it would error off the bat so maybe I should actually test Yanfly's script with my durability...

Ok, I didn't see that coming, thanks for catching that, I didn't take into consideration an equip scene embedded into the shop scene(or any scene really, but I can only think of shops doing so) where it would check "equability" of templates despite you specifically having instance items for that equip type. There are 2 possible fixes I can think of, the one you provided, and initializing durability for all items, not just instance. Either would work but I'm going with the one you provided, so thanks again.

@Roguedeus
Copy link

@Roguedeus Roguedeus commented Jan 26, 2014

Since it was dealing with whether the item was an instance already or not I came to the instance topic. I didn't think about it, but you're right about that. Hope Hime isn't irritated. :)

And you're welcome! I am feeling really useful now, all of a sudden. I am FINELY starting to understand Ruby like I was beginning to get C# and Java several years ago. All the feedback I get here has assisted tremendously. To the point where I can stop relying so much on others to do all the work, and start sharing some of it myself.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 26, 2014

I didn't think of items in the shop, but you're right those would typically be templates UNLESS you had a shop script that initialized instances.

One problem is when you "try on" your equip by scrolling over it. It shows you any increase or decrease in params or other things. By default, the player will be trying on a template item, which contradicts the assumption that all items/equips that pass through actors are instance items, because my assumption was that only stuff from the inventory would be checked, which ARE properly normalized.

Or, of course, I could just make it so that shop items are all normalized.
I don't know what the consequences of that would be if you started having "random" attributes being generated for instance items, but one of them would be the shop items constantly having different stats.

In some games, they would have shop items refresh over a certain interval, so it would make sense. But in order to implement that in RM, you need to modify the shop to keep track of whether it should be refreshed or not (as well as a list of items that is available for sale from before)

@Selchar
Copy link

@Selchar Selchar commented Jan 26, 2014

As I've shown with my Random Equip Affixes, it's possible to limit bonuses gained to certain scenes. The original intent was to prevent affixes from being applied to every equip purchased in that script, but was expanded for any scenes you can come up with(such as initial equips through title scene, and battle/map scene acquisitions). This still allows for you to have an "Affixed Equip Shop" or rather a "instance initialized" shop, and the default "template" shops. Any script that adds(or modifies) stats upon instance creation could make use the same setup.

My only concern with shops that'd initialize instance items for display would be the eventual size of save files, which "refreshing" over defined intervals instead of whenever the shop is opened would slow down at the very least.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 27, 2014

Save files are going to become massive over-time, which is another issue that will have to be resolved.
This affects any instance object anywhere in the game (inventory, shops, chests maybe, etc.)

Currently, the engine does a naive marshal dump of entire objects.

The reason why it works for the default engine is the way it's structured: you have your database files stored separately, and all of the references to the objects are done through ID's. This allows you to have 99 hand axes while consuming maybe 8 bytes: one for the count, and one for the ID. Plus some overhead for the hash maybe.
Anyways, it's really small.

However, once you start mixing instance objects into the game, you need to store them somewhere, and a reasonable place to put them is the save file since they are associated with that particular instance of the game.

There are better ways to store instance objects of course. One, is to only store the attributes, possibly in a hash. For example, instead of storing an RPG::Weapon object, you just store its name, parameters, and features, and so on, and during load-time you would re-build the object from that data.

I will have to do some benchmarking to collect some data on file size.

Another thing I noticed in SES instance items is something called "anti-bloat". The reason why that works is because the instances in that script are not added to the database. However, that is accomplished by rewriting most of the code that involves inventory management.

It is something to consider, of course. I don't know about its compatibility but my design is not very efficient.

@Selchar
Copy link

@Selchar Selchar commented Jan 27, 2014

I had the thought of keeping track of the id's of instance items destroyed in an array, and using said array to assign new instance items an id first, and if the resulting id is nil, then use the default method of table.size to assign the item id. I did a preliminary test of such a method and it seems to work. I haven't found any problems in doing it. All the while I can create 99 items, then lose them repeatedly without the save file size going up. It only seems to goes up when actual new id's are created.

I'm not sure if such a way of doing it would conflict with anything, but it's worth giving it some thought.

Here's what I think is a finished version of what I was suggesting. Hope it helps.
https://drive.google.com/file/d/0BywhvOtY4_H9NEc0aS12Z0JBWVk/edit?usp=sharing

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 27, 2014

What I think is an issue is what it means for an item to be "destroyed".

Initially, I had thought something on the lines of: "well if you lose an item, then it's gone", but then you have to think about where the item is going.

Because all instance items are unique, we can assume that there can only be at most one copy of it floating around. Therefore, if we only consider the inventory (or anywhere within the party), then yes, if the item is not in the inventory, then it is effectively not in the game.

(This is an assumption that I have provided based on my design. The assumption is not necessary if there was no need to reference instance items by ID, however the rest of the engine uses ID's so I figured that might be a better option.)

However, if we extend our scope to include other possible scripts that interact with the rest of the game, it's not a valid assumption.

Take for an example a storage system where you place your items in a chest for safe keeping. This doesn't have to be a special storage place, it could just be an arbitrary chest that you find in the middle of a dungeon. The party has "lost" the item as it is no longer in its inventory, but it still exists in the game.

Another example is when you sell your item to a shop, and the shop allows you to buy it back (at a higher cost, of course). While this may sound like a strange mechanic, it is not impossible.

Or when you can drop an item on the map and pick it up again. Just because you drop an item doesn't mean it's gone forever.

In these cases, all of the mechanics would be logically implemented by assuming the item has left the party. I have seen implementations of "external" storage systems where the storage data is...stored with the party. That is kind of strange to me. You can't store everything with the party if the party has nothing to do with it. I'm sure there are mechanics where it makes no sense to have the party keep track of it.

However, it is likely impossible to solve the problem if we limit ourselves to a very general solution that would consider all possible situations. If we consider the default engine, that is a perfectly valid solution since only the party keeps track of the inventory.I like the idea of keeping track of an array of deleted ID's, especially because it uses the fact that if an item is lost, then it no longer exists.

The way that I solved applying instance attributes was done by providing methods specifically for you to alias, which has definitely increased compatibility between all plugins for it.

Maybe there would be some way for other scripts to be written to override the delete flag when necessary, and if someone actually needs it so that our assumption about losing an item == destroyed is wrong, it would be a simple compatibility patch or a quick method alias.

@Selchar
Copy link

@Selchar Selchar commented Jan 28, 2014

The script I posted above can do that, by simply aliasing Game_System add_deleted_item_id, you can add conditions such as specific scenes. On that note it took me an entire day of on and off testing to realize that by default there needed to be an exception condition for if the current scene is Scene_Equip. Storage scripts can easily be supported(I tested Galv;s and Dekita's, tho I don't recommend Dekitas since my weapons went into "item" storage for some reason), as well as shops with buy back options. Dropping items onto the map however would require a slightly more complicated patch I think.

It's been updated with a hash for simple scene checks, just like my random affix script, as well as a separate method to handle conditions to be aliased if anything more is needed.

@HimeWorks
Copy link
Member Author

@HimeWorks HimeWorks commented Jan 28, 2014

A scene check would be a start for basic validation, but as you have pointed out it becomes an issue when you're in a scene where losing an item may or may not mean the item is destroyed. Three simple use cases, with different script setups:

  1. Map drops enabled, so dropping items on the map = not destroyed (immediately)
  2. Map drops with decay enabled, so dropped items disappear after some time elapses. The item is then flagged as destroyed.
  3. Map drops not enabled, so dropping items (if supported) = flagged as destroyed.

These are considered to be standard "drop" mechanics, since I think there are only so many ways you can drop something from your inventory (the map is just a particular destination. It could be sent to your storage, for example).

In these cases, the scene check would not be sufficient, and if we're going to start introducing a bunch of exception checks, it may be better to see if there are some alternatives to handle it, particularly because I really don't want to clutter scripts with a bunch of instance item deletion logic.

The scene checking would allow you to easily add in a small patch to indicate flagging behavior.
Perhaps something like this would handle the cases where a blanket check does not work

## Assuming you view your inventory on the same scene
# Deletes an item from the inventory
def discard_item( ... )
    InstanceManager.delete_flag = true
    $game_party.lose_item( ... )
    InstanceManager.delete_flag = false
end

# Drops an item from the inventory
def drop_item
   InstanceManager.delete_flag = false
   $game_party.lose_item( ... )
end

This assumes they are fairly atomic transactions. That is, when you say you want to lose an item, that is the only item you will lose.

It would NOT work if losing an item will cascade itself and cause you to lose another dozen items, but adding in these flags can be done with simple aliases without too much hassle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.