[SOLVED]Creating and interacting w "custom" fields

edited February 2012 in Help request
Hey Iarwain. I have another quick question for you. We want some of our objects - namely the enemies - to have custom fields representing max health and damage taken. The question is: how do we make these custom fields (preferably in the INIs)?

The biggest problem is that every enemy will be created from config with the same object. Is there a way to access these fields from the pointer to a created object? Is there a better way to create fields like this in general?

Comments

  • edited January 2012
    Hi sonicbhoc,

    I guess you need something like this:
    orxConfig_PushSection("Enemy");    
    for(orxS32 i = 0; i < orxConfig_GetListCounter("EnemyCount"); ++i)
    {
        orxConfig_GetListString("EnemyTypes", i);
    }
    orxConfig_PopSection();
    

    Basically you can just add your custom fields without any problems:
    [Enemy]
    Health = 100
    ...
    

    And later you can Get/Set them using the functions provided in the orxConfig space.

    HTH,
    Alex
  • edited January 2012
    Thanks for that. The problem I'm really having, though, is:
    For each enemy created with the orxObject_CreateFromConfig function, each enemy will have 100 HP. What's the best way to keep track of each enemy's individual HP? I don't think I can keep using the orxConfig_Get/SetListString functions in that case.

    EDIT: And what's UserData, and how do I use it? I think it might be just what I'm looking for.
  • edited January 2012
    Yep, orxObject_SetUserData lets you pass a void *, so you could cast anything you want to void * (like an structure containing your custom fields) and cast it back to your type when you retrieve it using orxObject_GetUserData.
  • edited January 2012
    I'm not sure but i think any orxConfig function is per object instance not one for all objects of given type. As you suggested you can indeed use UserData but I think that you can use the easier way: just get a float value from Health config variable of your enemy instance and that should work fine. At least it's worth a quick check :-)
    My Get/Set list example is just a quick copy/paste from my project. I'm sorry if that confused you :-)

    Good luck!
  • edited January 2012
    orxConfig seems to read directly from the file; there doesn't seem to be any way to specify an instance of an object. Looks like UserData is the way to go, at least for now. I'll talk more about it with the team. I don't know if anyone else has any ideas.
  • edited January 2012
    Hmm,

    I give you some tricks.

    orxConfig_XXX functions does "what you want" :)

    I think the best example for your problem is Scroll (you can find informations about it in an another post).

    Scroll use a combination of orxConfig_XXX function and orxObject_SetUserData to achieve exactly what you want.

    I'll try to explain, but I really recommend you to check out Scroll and observe the Iarwain's code :) Just by inspecting/testing this code, you will learn a lot !

    Scroll define a C++ class, called ScrollObject. An instance of ScrollObject refer to an object in the config module.

    Each instance of a Scroll object use a base section in the config, the same as yours (the section that describe your object), used and shared by all instances.

    But in addition, a scroll object create a "unique" section in the config module (this unique name is generated by scroll). So, each instance can keep values in it's "unique" section.

    The C++ instance of a scroll object is linked to its corresponding orxOBJECT using the function orxObject_SetUserData. So, when you have an orxOBJECT*, you can retreive the C++ instance by using the orxObject_GetUserData() function.

    I know my explanation are not very good and same for my english, hope that can help ... ^^
  • edited January 2012
    Wow. I thought these were not Scroll's extras but core orx functionality. Sorry for misleading you, OP :-) I only used orx with Scroll.. that makes life a lot easier ;)
  • edited January 2012
    I don't have the time to learn a new tool, unfortunately... release day is in April! Not to mention that mushroom stew and mushroom stew editor (which I'm assuming is scroll) both start processes and hang on my computer, never getting to the part where they open a window. Which is a shame, because I wanted to try mushroom stew...

    EDIT: Right after saying that, I got it to load. Ignore that, I'm going to try this game out now.
  • edited February 2012
    Well, everything has been said here so to sum up: orxConfig is object-agnostic, it's only a repository of data in memory.
    Orx uses it to drive the creation of some structures (sound, fx, graphics, objects, ...) and so can the user do on his end.

    The concept of unique ID for objects is implemented in Scroll, not in orx, but it's easy to roll your own if you need it.

    Here's a small example of how to achieve HP for enemies. Of course you can use C++ classes and use inheritance and whatnot, it's really a minimal example to show how to use the user data part of objects.
    
    In config:
    
    [EnemyBase]
    IsEnemy = true
    
    [StandardEnemy@EnemyBase]
    Health = 100
    
    [BigEnemy@EnemyBase]
    Health = 200
    
    In code:
    
    typedef struct Enemy
    {
      orxOBJECT *pstObject;
      orxS32     s32Health;
    } Enemy;
    
    orxBANK *spstEnemyBank;
    
    orxSTATUS orxFASTCALL Init()
    {
      spstEnemyBank = orxBank_Create(128, sizeof(Enemy), orxBANK_KU32_FLAG_NONE, orxMEMORY_TYPE_MAIN);
    
      return orxSTATUS_SUCCESS;
    }
    
    Enemy *CreateEnemy(const orxSTRING _zEnemyType)
    {
      orxOBJECT *pstObject;
      Enemy *pstEnemy;
    
      // Creates both user and orx sides of the enemy
      pstEnemy = (Enemy *)orxBank_Allocate(spstEnemyBank);
      pstObject = orxObject_CreateFromConfig(_zEnemyType);
    
      // Binds them together
      orxObject_SetUserData(pstObject, pstEnemy);
      pstEnemy->pstObject = pstObject;
    
      // Inits life
      orxConfig_PushSection(_zEnemyType);
      pstEnemy->s32Health = orxConfig_GetS32("Health");
      orxConfig_PopSection();
    
      return pstEnemy;
    }
    
    // Let's say we have a function that tries to apply damage to an orxObject
    void ApplyDamage(orxOBJECT *_pstObject, orxS32 _s32Damage)
    {
      // Is an enemy?
      orxConfig_PushSection(orxObject_GetName(_pstObject));
      if(orxConfig_GetBool("IsEnemy"))
      {
        Enemy *pstEnemy;
    
        pstEnemy = (Enemy *)orxObject_GetUserData(_pstObject);
    
        // Updates health
        pstEnemy->s32Health -= _s32Health;
      }
    }
    
  • edited February 2012
    Thanks! I'll pass this along.
  • edited February 2012
    Good information.

    I made use of this tonight.

    In my level select screen, I wanted to create a large number of similar icons to represent the different levels. I preferred to define only one icon object in the config, of course, but I needed to track which icon selects which level when tapped.

    So the level number is generated and stored in a config for each icon as it is created and the config section name is stored as the user data for the icon.
  • edited February 2012
    Nice idea. :)

    But if I understand correctly, if you wanted it you could have eliminated the need of using the UserData field altogether.

    Something like:
    
    In config
    
    [Game]
    LevelNumber = ...
    
    [LevelIcon]
    ; Here's your icon settings, common to all instances
    
    In code:
    
    void InitLevelMenu()
    {
      orxU32 u32LevelNumber;
    
      orxConfig_PushSection("Game");
      u32LevelNumber = orxConfig_GetU32("LevelNumber");
      orxConfig_PopSection();
    
      for(orxU32 i = 0; i < u32LevelNumber; i++)
      {
        orxCHAR acBuffer[256];
        orxOBJECT *pstIcon;
    
        // Gets the config name for this instance of the level icon
        orxString_NPrint(acBuffer, 256, "LevelIcon%d", i);
    
        // Creates the config section and gets into it
        orxConfig_PushSection(acBuffer);
    
        // Sets its "Level" property, it can be the level index like done here
        // or it can be the config name of the level made from concatenation
        // or taken from a level list stored in the Game section
        orxConfig_SetU32("Level", i);
    
        // Updates whichever config icon instance properties we want (like pos, etc...)
        ...
    
        // Now sets "LevelIcon" as parent as our instance section to inherit from all the base properties
        orxConfig_SetParent(acBuffer, "LevelIcon");
    
        // We're done with config manipulations
        orxConfig_PopSection();
    
        // Creates the icon object
        pstIcon = orxObject_CreateFromConfig(acBuffer);
    
        // Updates whichever live icon instance properties we didn't want to set in config
        ...
      }
    }
    
    // Upon picking an object from the mouse click
    void OnPick(const orxOBJECT *pstPickedObject)
    {
      ...
    
      // Pushes its config section
      orxConfig_PushSection(orxObject_GetName(pstPickedObject));
    
      // Has Level property?
      if(orxConfig_HasValue("Level"))
      {
        // Yeah, it's an icon, use the value of that property to jump to the matching level
      }
    
      orxConfig_PopSection();
    }
    
    
  • edited February 2012
    Hmm, that's a really good idea. The orxConfig_SetParent part is what I was missing.

    I was looking for a SetName function to pick the particular icon, but SetParent should work for this and should fit right in with my architecture.

    Off-topic but related, I have used the config system with great success for creating a kind of menu/dialog manager for my game. I have a "Type" property to define a button or label, for example. And I set properties like "To"/"From" or "Action"/"Target" in config.

    So in code, I can perform functions like switching between menus and making a mouse click on a menu button increase the value of a label.

    And the code is abstract, so it doesn't have to know anything about the specific menu it's using.
  • edited February 2012
    I like that!
    If you ever feel like it, that could be a nice thing to have in the wiki, in the community tutorial section. ;)

    That reminds me of a more limited system I made for buttons in ScrollEd where buttons are generic config section with "IsButton"/"Action"/"HighLight" fields.
    The "Action" field is then used to set a virtual input with orxInput_SetValue() when the button is clicked.
    This way one can add, at will, buttons or any key shortcuts to any "Action" recognized by the editor without having to touch the code.
  • edited February 2012
    Yes, there are a few things I would like to add to the tutorial section.

    The main reason I have not written any tutorials yet is everything is still evolving so quickly in my project :)

    But we are getting close to a beta, so things are stabilizing a bit and I might write something then.

    Which reminds me, I need to start a thread in the projects section...
  • edited February 2012
    Nice!

    Looking forward to learning more about your project. :)
Sign In or Register to comment.