Animation Property and Collision Development

DCurrent

Site Owner, OpenBOR Project Leader
Staff member
Progress for Animation Properties...

  • New getanimationproperty({animation handle}, {property}, { frame (optional)}) is in progress works great. It only accepts an animation pointer, integer animation property, and an optional frame. If frame is omitted, frame defaults to 0. NO sub property support... Instead you will use this to get a handle for the sub property and and access that sub property with its own specific function.
  • Notice I said integer for {property}. I have eliminated mapstrings for animation properties entirely.  Instead the property enumerator is now part of openbor.h, and all animation property constants are accessible through openborconstant("ANI_PROP_...").

All that remains now is to fill in the switch statement and begin working on the sub property functions - all of which will operate under the same principal.

Code:
HRESULT openbor_getanimationproperty(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount)
{

    #define SELF_NAME           "getanimationproperty({animation handle}, {property}, {frame (optional)})"
    #define ARG_MINIMUM         2   // Minimum required arguments.
    #define ARG_ANIMATION       0   // Animation handle.
    #define ARG_PROPERTY        1   // Animation property.
    #define ARG_FRAME           2   // Optional animation frame.

    int                     result          = S_OK; // Success or error?
    s_anim                  *animation      = NULL; // Animation handle.
    e_animation_properties  property        = 0;    // Animation property supplied by string argument and enumerated from property list.
    int                     frame           = 0;    // Optional frame for properties with per frame instances.

    // Clear pass by value argument and map strings to animation property list.     .
    ScriptVariant_Clear(*pretvar);

    // Verify incoming arguments. There should at least
    // be a pointer for the animation handle and an integer
    // animation property.
    if(paramCount < ARG_MINIMUM
       || varlist[ARG_ANIMATION]->vt != VT_PTR
       || varlist[ARG_PROPERTY]->vt != VT_INTEGER)
    {
        printf("You must provide a valid animation handle and property: " SELF_NAME "\n");
        result = E_FAIL;

        // No point in doing anything else, exit here.
        return result;
    }
    else
    {
        animation   = (s_anim *)varlist[ARG_ANIMATION]->ptrVal;
        property    = (LONG)varlist[ARG_PROPERTY]->lVal;
    }

    // Get frame argument if provided.
    if(varlist[ARG_FRAME]->vt != VT_EMPTY)
    {
        if(varlist[ARG_FRAME]->vt == VT_INTEGER)
        {
            frame = (LONG)varlist[ARG_FRAME]->lVal;
        }
        else
        {
            // User sent invalid type for frame argument.
            // No reason to shut down. We'll just default to
            // frame 0 and send an alert to log.
            printf("Optional {frame} argument is invalid. Defaulting to frame 0: " SELF_NAME "\n");
        }
    }

    // Most property values are integers. Set type here for less repetition.
    ScriptVariant_ChangeType(*pretvar, VT_INTEGER);

    // Which animation property to get?
    switch(property)
    {
        case ANI_PROP_ANIMHITS:

            (*pretvar)->lVal = (LONG)animation->animhits;
            break;

        case ANI_PROP_ANTIGRAV:

            (*pretvar)->lVal = (LONG)animation->antigrav;
            break;

        case ANI_PROP_NUMFRAMES:

            (*pretvar)->lVal = (LONG)animation->numframes;
            break;

        default:

            printf("Unsupported animation property: " SELF_NAME "\n");
            result = E_FAIL;
            break;
    }

    return result;

    #undef SELF_NAME
    #undef ARG_MINIMUM
    #undef ARG_ANIMATION
    #undef ARG_PROPERTY
    #undef ARG_FRAME
}
 
Further progress:

-get/set animation property is in place (not stable yet).
-Got attack properties in place as a template.
-Attack and animation constants added. This replaces the clumsy mapstring system currently in use for most types of property access. This effectively moves the string mapping over to openborconstant(), which we can eventually make part of a pre-processor routine.

I had hoped to keep all function calls to two or less parameters by braking down sub properties as follows:

entity->animation->frame->property

Unfortunately, there is no such thing as frame handle. Instead, properties within an animation that have a per frame instance are keyed arrays.

Code:
animation->property[frame]

I don't like this design at all. Frames should have their own structure and pointer in my opinion, but there's nothing I can do about it. It would take essentially rewriting the whole engine to change the current design. We'll have to settle for forcing the user to include frames on getanimationproperty.

Code:
getanimationproperty(void handle, int frame, int property)
setanimationproperty(void handle, int frame, int property, value)

Not all properties require a frame index, but many do. Moving the frame argument next to animation handle and making it required will make sure function calls are consistent, and that there is no confusion between value and frame when setting vs. getting.

Also in progress: In properties, for example, the bbox, are structures but do not have pointer assignment. I am working on fixing this before I can move forward with opening them to animation properties.

 
As of ver. 4244, bboxes are now allocated with their own pointers identically to attack boxes. Should have done that a long time ago. In addition to allowing access through handles in script, it gives us a couple of other benefits.

  • May save some memory. I'm a bit confused honesty as to how the previous allocation worked, but if I was reading it correctly, every animation frame had a structure of the entire bbox allocated, used or not. Now there is only a pointer, and memory is allocated for the structure if (and only if) a bbox is called for. Worst case scenario, memory consumption is static.
  • Consistency. Already talked about above - every sub property should be allocated and accessed the same way.
  • Versatility. With the body box now its own structure, I am looking at adding new features. Specifically, a tag as with attack, and per bbox defense settings. Authors should really love the later. Eventually I'd like to combine attacks and bboxes into a singular collision box setting for even more versatility and consistency, but that's another one of those things that would be more of a re-write than an upgrade.

Now that I know the concept works, I'll be continuing to do the same with all the other sub properties of animations.
 
Woohooo! I finally figured out a way to get rid of the {frame} argument and distill it down to the sub-properties where it belongs. Actually feel stupid not realizing it sooner.

As mentioned before OpenBOR has no frame structure. Animations properties that exist on a per frame basis are essentially arrays attached to the animation structure, with their keys being the frame. Again, I HATE this design, but it's what we have, it works, and changing it is not worth the effort right now.

What this means is I can't create a generic frameproperty() function, but I can create a frame based access for the individual sub properties. I'm using attack as the template for all others - and while I'm at it, making sure there is accommodation for multiple attacks per frame. Here's how it works:

Animation Property

Animation properties are finally distilled down into two consistent control arguments. No sub arguments, no optional arguments, no frame that sometimes does nothing.

For animation level properties without any sub-properties, this is all you need:

Code:
void    handle      = getentityproperty({entity}, "animation.handle", {animation ID});
int     property    = openborconstant("ANI_PROP_<some property name>");


getanimationproperty(handle, property);

void value = <some new value>;
setanimationproperty(handle, property, value);

Attack Property

Attack property is the template for all sub-properties. For each breakdown, we get a handle for the next level, until we get to the specific property. Here's how:

-First, we get the handle for all attacks using the animation property access, like this. Remember, attacks for each animation are an array using frames as the key.
Code:
int property = openborconstant("ANI_PROPERTY_ATTACK");
void animation_attacks_handle = getanimationproperty({handle}, property);

-Now that we have the array of attacks for the animation, we can get an individual frame's attack collection handle. Note the word collection - get to that in a second.
Code:
int frame = 0;
void attack_collection = getattackcollection(animation_attacks_handle, frame);

-We now have an attack collection. Now we just need to get a single item handle from said collection. This is what will allow access to multiple attacks per frame. Index is the attack instance you want to to access.
Code:
int index = 0;
void attack_handle = getattackinstance(attack_collection, index);

-Once you have the handle for an individual attack instance, you can then do whatever you like with it. So long as you have the handle and the attack actually exists (as in, the entity still exists), you can continue to access it with said handle.

Code:
int attack_property = openborconstant("ATK_PROP_<some attack property>");
void some_property_value = getattackproperty(attack_handle, attack_property)

void new_value = <some new value>
setattackproperty(attack_handle, attack_property, _new_value);

Here's a full example, showing how you might get the damage force for the second frame of attack 2 animation, and increase it by 20.

Code:
void    entity; 
void    animation_handle;
void    attack_collection;
void    attack_frames_collection;
void    attack_handle;
int     attack_property_force;
float   attack_porperty_kdv_x;
float   attack_porperty_kdv_y;
int     frame;
int     index;

// Get calling entity.
entity = getlocalvar("self");

// Get the animation handle.
animation_handle    = getentityproperty(entity, "animation.handle", openborconstant("ANI_ATTACK2"));

// Get handle of attacks collection for animation.
attack_frames_collection = getanimationproperty(animation_handle, openborconstant("ANI_PROP_ATTACK"));

// Get handle of attack collection from second frame of animation.
frame = 1;
attack_collection = getattackcollection(animation_attacks_handle, frame);

// Get handle of first instance from attack collection.
index = 0;
attack_handle = getattackinstance(attack_collection, index);

// Get the attack properties.
attack_property_force = getattackproperty(attack_handle, openborconstant("ATK_PROP_DAMAGE_FORCE"));
attack_property_kdv_x = getattackproperty(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_X"));
attack_property_kdv_y = getattackproperty(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_Y"));

// Adjust damage and knockdown a bit. Let's hit HARDER!
attack_property_force += 20;
attack_property_kdv_x += 3.0;
attack_property_kdv_y += 1.5;

// Apply the new property values.
setattackproperty(attack_handle, openborconstant("ATK_PROP_DAMAGE_FORCE"), attack_property_force); 
setattackproperty(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_X"), attack_property_kdv_x); 
setattackproperty(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_Y"), attack_property_kdv_y);
 
Ran smack into a memory allocation issue while working on multiple collisions boxes, and won't be able to work on it till Monday. Just keeping you guys up to date that I won't be putting up any subversions for a few days.

DC
 
Went to the drawing board and wrote a self contained simulator program that allocates "animations" just like OpenBOR. That way I could play around with the memalloc and not have to go through several minute process of compiling, copying the new binary to a module folder, loading module, wait for crash, repeat...

Finally got it working, and will see if I can adopt it to the engine in next couple of days:

Code:
#include <stdlib.h>
#include <stdio.h>

typedef struct
{
    int test_int;
} s_attack;

typedef struct
{
    s_attack **attacks;
}s_attack_list;

typedef struct
{
    s_attack_list **attacks;
    int test_int;
} s_anim;

typedef struct
{
    s_anim **animations;
} s_entity;


#define ANIMATIONS  400
#define FRAMES      40
#define ATTACKS     10
#define INDEXES     4

int main(void)
{
    int animation;
    int frame;
    int index;

    printf("sizeof s_entity:\t %u bytes\n", sizeof(s_entity));
    printf("sizeof s_anim:\t %u bytes\n", sizeof(s_anim));

    // Pointer vars
    s_entity      *s_entity_ptr;

    // Allocate space for the main structure and return the pointer.
    s_entity_ptr = malloc(sizeof(s_entity));

    // Allocate an array of animation pointers
    s_entity_ptr->animations = malloc(ANIMATIONS * sizeof(*s_entity_ptr->animations));

    // Allocate memory for an array of attacks lists for every frame each animation.
    for(animation=0; animation<ANIMATIONS; animation++)
    {
        // Allocate the space for the animation structure on each animation.
        s_entity_ptr->animations[animation] = malloc(sizeof(**s_entity_ptr->animations));

        // Allocate a pointer for attack frame list on each animation.
        s_entity_ptr->animations[animation]->attacks = malloc(FRAMES * sizeof(*s_entity_ptr->animations[animation]->attacks));

        for(frame=0; frame<FRAMES; frame++)
        {
            // Allocate a pointer for attack index list on each animation frame.
            s_entity_ptr->animations[animation]->attacks[frame] = malloc(sizeof(**s_entity_ptr->animations[animation]->attacks));
            
            // Allocate space for indexed list of attacks.
            s_entity_ptr->animations[animation]->attacks[frame]->attacks = malloc(INDEXES * sizeof(*s_entity_ptr->animations[animation]->attacks[frame]->attacks));

            for(index=0; index<INDEXES; index++)
            {
                // Allocate space for attack structures on each index.
                s_entity_ptr->animations[animation]->attacks[frame]->attacks[index] = malloc(sizeof(*s_entity_ptr->animations[animation]->attacks[frame]->attacks[index]));
            }
        }
    }


    printf("sizeof s_entity_ptr:\t %u bytes\n", sizeof(s_entity_ptr));
    printf("sizeof s_entity_ptr->animations:\t %u bytes\n", sizeof(s_entity_ptr->animations));
    printf("sizeof ANIMATIONS * sizeof(*s_entity_ptr->animations):\t %u bytes\n", ANIMATIONS * sizeof(*s_entity_ptr->animations));

    printf("sizeof FRAMES * sizeof(s_entity_ptr->animations[animation]->attacks):\t %u bytes\n", FRAMES * sizeof(s_entity_ptr->animations[animation]->attacks));


    /*
    printf("s_entity_ptr->animations[0]->test_int value:\t %d \n", s_entity_ptr->animations[0]->test_int);
    printf("s_entity_ptr->animations[1]->test_int value:\t %d \n", s_entity_ptr->animations[1]->test_int);
    printf("s_entity_ptr->animations[2]->test_int value:\t %d \n", s_entity_ptr->animations[2]->test_int);
    */
}

Also, I'm moving this thread to the public area of Engine just so members can see progress.
 
Previously it was possible to do this :

Code:
getentityproperty(self, "attack", "dropv", 0, ani, getentityproperty(self, "animpos"), "x");

Is there an equivalent or it is not finished yet ?
 
nsw25 said:

These are my coding development notes, so obviously I'm not going to waste time explaining every detail. However, I can answer your question... no.

What I am working on atm is multiple collision boxes per frame and simplified function calls for animation properties that don't need 50 inconsistent parameters to work right.

Piccolo said:

I've depreciated those calls, and they aren't coming back. When White Dragon added them, he was using an ancient template I started and never intended to release until I could get it rewritten because it's not sustainable moving forward.

The new calls will be much more powerful and future proof, not to mention simpler once you get used to them. You get a handle to the property instance, and select the property type with an openborconstant. Then you can get/set the value at will.

Example (note entity property still uses old style)
Code:
// get animation handle with entity property.
void animation = getentityproperty(entity, "animation.handle", openborconstant ("ATTACK1"));

// get and set a property.
property = getanimationproperty(animation, openborconstant("ANI_PROP_WHATEVER"));

setanimationproperty(animation, openborconstant("ANI_PROP_WHATEVER"), new_value);
 
Well actually I was using this way back before White Dragon got into the engine development.

But anyways, I understand why it's been deprecated, hope the new system will be available soon  ;)
 
Piccolo said:
Well actually I was using this way back before White Dragon got into the engine development.

Well, I guess added isn't the best word - he finished them. I say added because frankly, I only did a few, and he put in the whole dang list. Either way, it was a cool thing to do, but there was a reason I never completed them as is.

Longer, and longer function calls with mapstrings on top of mapstrings just isn't maintainable long run, and it's nearly impossible to document. Plus it just makes for crappy, inconsistent code - both in the engine and in scripts. Some of you might not care "as long as it works"... but that's the problem. If you don't have a good foundation and keep piling stuff on, sooner or later, it just won't.

The new way of doing things also opens up a whole slew of options - but I'll get to that later. Just having a clean interface is motivation enough for now. ;)

DC
 
Damon Caskey said:
Longer, and longer function calls with mapstrings on top of mapstrings just isn't maintainable long run, and it's nearly impossible to document. Plus it just makes for crappy, inconsistent code - both in the engine and in scripts. Some of you might not care "as long as it works"... but that's the problem. If you don't have a good foundation and keep piling stuff on, sooner or later, it just won't.

I totally agree with you on that. Actually I remember that I struggled to understand how the function worked, and did some testing to get the proper call I wanted.

Making things just work is great when experimenting and testing stuff. But when building proper solutions, you got to take some time to... well, make things properly ! Always worth it.
 
I see these functions are accessible in latest builds, but is the code finished yet ?

I'm wondering because I only get 0 values right now.

Code:
	void    handle      = getentityproperty(vEnt, "animation.handle", getentityproperty(vEnt, "animationID"));
	int     property    = openborconstant("ANI_PROP_ATTACK");
	void animation_attacks_handle = get_animation_property(handle, property);
	
	void attack_collection = get_attack_collection(animation_attacks_handle, getentityproperty(vEnt, "animpos"));
	void attack_handle = get_attack_instance(attack_collection, 0);
	log("\ndamage : " + get_attack_property(attack_handle, openborconstant("ATTACK_PROP_DAMAGE_FORCE")));
 
Piccolo said:
I see these functions are accessible in latest builds, but is the code finished yet ?

I'm wondering because I only get 0 values right now.

Code:
	void    handle      = getentityproperty(vEnt, "animation.handle", getentityproperty(vEnt, "animationID"));
	int     property    = openborconstant("ANI_PROP_ATTACK");
	void animation_attacks_handle = get_animation_property(handle, property);
	
	void attack_collection = get_attack_collection(animation_attacks_handle, getentityproperty(vEnt, "animpos"));
	void attack_handle = get_attack_instance(attack_collection, 0);
	log("\ndamage : " + get_attack_property(attack_handle, openborconstant("ATTACK_PROP_DAMAGE_FORCE")));

No, they are not finished, though they will be soon. I need to finish the multiple boxes first. Should be all done by end of this coming week.

DC
 
Sorry man, school and work both kicked back into high gear and halted my OpenBOR time. It should calm down in a couple of weeks and I'll get back on it.

DC
 
No problem, I don't really have much OpenBOR time either these days, but as I have some pending stuff related to this I was just checking the status to know if I could get back to it. That can wait.
 
Hey,

I saw that the functions are now are available on the latest revs.

Though I tried and could not get attack properties values.

Code:
	void    handle      = getentityproperty(vEnt, "animation.handle", aniID);
	int     property    = openborconstant("ANI_PROP_ATTACK");
	void animation_attacks_handle = get_animation_property(handle, property);
	
	void attack_collection = get_attack_collection(animation_attacks_handle, getentityproperty(vEnt, "animpos"));
	void attack_handle = get_attack_instance(attack_collection, 0);

	log("\ndamage " + get_attack_property(attack_handle, openborconstant("ATTACK_PROP_DAMAGE_FORCE"))
	int dropX = get_attack_property(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_X"));

Is the implementation not fully developped yet ?
 
Piccolo said:
Hey,

I saw that the functions are now are available on the latest revs.

Though I tried and could not get attack properties values.

Code:
	void    handle      = getentityproperty(vEnt, "animation.handle", aniID);
	int     property    = openborconstant("ANI_PROP_ATTACK");
	void animation_attacks_handle = get_animation_property(handle, property);
	
	void attack_collection = get_attack_collection(animation_attacks_handle, getentityproperty(vEnt, "animpos"));
	void attack_handle = get_attack_instance(attack_collection, 0);

	log("\ndamage " + get_attack_property(attack_handle, openborconstant("ATTACK_PROP_DAMAGE_FORCE"))
	int dropX = get_attack_property(attack_handle, openborconstant("ATTACK_PROP_REACTION_FALL_VELOCITY_X"));

Is the implementation not fully developped yet ?

Not quite. I was making good progress, then work went nuts again. Will get back to it soon as I can.

DC
 
Back
Top Bottom