/*
###################################
#
# dxrubyfw.c Ver. 0.0.3
#
###################################
*/
#define DXRUBYFW_VERSION "0.0.3"

#include "ruby.h"
#include "dxruby.h"

static VALUE mDXRubyFw;     /* DXRubyFwモジュール   */
static VALUE cSprite;       /* スプライトクラス     */
static VALUE cMatrix;       /* 行列クラス           */
static VALUE cVector;       /* ベクトルクラス       */
static VALUE eDXRubyFwError;  /* 例外               */

#define DXRUBYFW_GET_STRUCT( c, v ) ((struct DXRubyFw##c *)DATA_PTR( v ))
#define DXRUBYFW_CHECK_TYPE( a, b ) \
        {if( TYPE( b ) != T_DATA || RDATA( b )->dfree != (RUBY_DATA_FUNC)a##_release )\
        rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRubyFw::"#a")", rb_obj_classname( b ));}
#define PI 3.141592653589793115997963468544185161590576171875f

static ID id_out;
static ID id_hit;
static ID id_shot;
static ID id_update;
static ID id_draw;
static ID id_delete_flag;

static ID id_set_x;
static ID id_set_y;
static ID id_set_dx;
static ID id_set_dy;
static ID id_set_speed;
static ID id_set_angle;
static ID id_rotate;
static ID id_rotate_image;
static ID id_accel;
static ID id_add_dx;
static ID id_add_dy;
static ID id_add_alpha;
static ID id_wait;
static ID id_call_command;
static ID id_delete;

static VALUE symbol_blend   = Qundef;
static VALUE symbol_angle   = Qundef;
static VALUE symbol_alpha   = Qundef;
static VALUE symbol_scalex  = Qundef;
static VALUE symbol_scaley  = Qundef;
static VALUE symbol_centerx = Qundef;
static VALUE symbol_centery = Qundef;
static VALUE symbol_z       = Qundef;
static VALUE symbol_add     = Qundef;
static VALUE symbol_add2    = Qundef;
static VALUE symbol_sub     = Qundef;
static VALUE symbol_sub2    = Qundef;

static float g_dx = 0.0f;
static float g_dy = 0.0f;

static struct DXRubyFwSprite {
    float x;
    float y;
    float z;
    float dx;
    float dy;

    short check_range_flag      :1;              /* 1で画面外チェックあり */
    short call_draw_flag        :1;              /* 1で描画時にdrawを呼ぶ */
    short call_update_flag      :1;              /* 1でupdate時にupdateを呼ぶ */
    short delete_flag           :1;              /* 1で衝突判定をしない。クラスメソッドcleanで配列から消える。 */
    short visible_flag          :1;              /* 1で描画する */
    short use_collision_flag    :1;              /* これが0だとQnilを格納しているということになる */
    short collision_enable_flag :1;              /* これが0だと↑が1でも判定はされない */
    short speed_angle_link_flag :1;              /* speed/angleがdx/dyと一致しているときに1になる */
    short image_sync_flag       :1;              /* 画像の角度と移動の角度を同期させるかどうかのフラグ */
    short anime_flag            :1;              /* 再生しているかどうか */
    char image_alpha;                            /* 画像のα値 */
    char blend;                                  /* ブレンド条件 */

    float image_angle;                          /* 画像の描画角度 */
    float image_scalex;                         /* 画像の横拡大率 */
    float image_scaley;                         /* 画像の縦拡大率 */

    VALUE image;                                /* image=で設定されたオブジェクト */
    VALUE anime_image;                          /* アニメ用イメージ配列 */
    VALUE anime_hash;                           /* アニメパターンオブジェクト */
    VALUE current_anime;                        /* 再生中のアニメパターン */
    VALUE command;                              /* コマンドの配列 */

    short range_x1, range_y1, range_x2, range_y2; /* 範囲外判定用のrange */
    short base_collision_x1, base_collision_y1, base_collision_x2, base_collision_y2; /* ユーザー指定衝突判定の相対範囲 */
    short collision_x1, collision_y1, collision_x2, collision_y2;                     /* 衝突判定処理に使う絶対範囲 */
    float speed;                                /* 速度 */
    float angle;                                /* 角度 */
    float image_turn;                           /* 自動で回転する1フレームあたりの角度 */
    float image_difference;                     /* 画像と移動角度の差 */
    int anime_count;                            /* アニメーションのカウント */
    int wait_count;                             /* ウェイトカウント */
    int pc;                                     /* コマンドのプログラムカウンタ */

//    int out_del_flag;                           /* 1でrangeから出たときにdelete_flagが1になる */
};


/* 行列 */
struct DXRubyFwMatrix {
    int x;
    int y;
    union {
        struct {
            float m11; float m12; float m13; float m14;
            float m21; float m22; float m23; float m24;
            float m31; float m32; float m33; float m34;
            float m41; float m42; float m43; float m44;
        };
        float m[4][4];
    };
};

/* ベクトル */
struct DXRubyFwVector {
    int x;
    union {
        struct {
            float v1; float v2; float v3; float v4;
        };
        float v[4];
    };
};


static VALUE hash_lookup(VALUE hash, VALUE key)
{
    VALUE val;

    if (!RTEST(hash) || !RHASH_TBL(hash) || !st_lookup(RHASH_TBL(hash), key, &val)) {
        return Qnil;
    }
    return val;
}


/*********************************************************************
 * Spriteクラス
 *
 * 当たり判定用の矩形範囲。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Sprite_release( struct DXRubyFwSprite *sprite )
{
    free( sprite );
}


/*--------------------------------------------------------------------
   Spriteクラスのmark。GCのマーク時に呼ばれる。
 ---------------------------------------------------------------------*/
static void Sprite_mark( struct DXRubyFwSprite *sprite )
{
    rb_gc_mark( sprite->image );
    rb_gc_mark( sprite->anime_hash );
    rb_gc_mark( sprite->current_anime );
    rb_gc_mark( sprite->anime_image );
    rb_gc_mark( sprite->command );
}


/*--------------------------------------------------------------------
   Spriteクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Sprite_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFwSprite *sprite;

    /* Spriteのメモリ取得＆オブジェクト生成 */
    sprite = malloc( sizeof(struct DXRubyFwSprite) );
    if( sprite == NULL ) rb_raise( eDXRubyFwError, "メモリの取得に失敗しました - Sprite_allocate" );

    sprite->image = Qnil;
    sprite->anime_hash = Qnil;
    sprite->current_anime = Qnil;
    sprite->anime_image = Qnil;
    sprite->command = Qnil;

    sprite->x = 0.0f;
    sprite->y = 0.0f;
    sprite->z = 0.0f;
    sprite->dx = 0.0f;
    sprite->dy = 0.0f;
    sprite->check_range_flag = 0;
    sprite->delete_flag = 0;
    sprite->call_draw_flag = 0;
    sprite->call_update_flag = 1;
    sprite->range_x1 = 0;
    sprite->range_y1 = 0;
    sprite->range_x2 = 0;
    sprite->range_y2 = 0;
    sprite->image_angle = 0.0f;
    sprite->image_scalex = 1.0f;
    sprite->image_scaley = 1.0f;
    sprite->image_alpha = 255;
//    sprite->out_del_flag = 0;
    sprite->base_collision_x1 = 0;
    sprite->base_collision_y1 = 0;
    sprite->base_collision_x2 = 0;
    sprite->base_collision_y2 = 0;
    sprite->collision_x1 = 0;
    sprite->collision_y1 = 0;
    sprite->collision_x2 = 0;
    sprite->collision_y2 = 0;
    sprite->visible_flag = 1;
    sprite->use_collision_flag = 0;
    sprite->collision_enable_flag = 1;
    sprite->speed = 0;
    sprite->angle = 0;
    sprite->speed_angle_link_flag = 0;
    sprite->image_turn = 0;
    sprite->image_sync_flag = 0;
    sprite->image_difference = 0;
    sprite->anime_count = 0;
    sprite->anime_flag = 0;
    sprite->wait_count = 0;
    sprite->pc = 0;
    sprite->blend = 0;

    obj = Data_Wrap_Struct( klass, Sprite_mark, Sprite_release, sprite );

    return obj;
}


/*--------------------------------------------------------------------
   内部の自動移動
 ---------------------------------------------------------------------*/
static VALUE Sprite_internal_update( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( sprite->delete_flag != 0 )
    {
        return Qnil;
    }

    /* コマンド実行 */
    if( sprite->command != Qnil )
    {
        while( sprite->wait_count == 0 )
        {
            VALUE *com;
            ID id;

            /* 次のコマンド */
            sprite->pc++;

            /* コマンドの最後までいった */
            if( sprite->pc >= RARRAY_LEN( sprite->command ) )
            {
                sprite->pc = 0;
            }

            com = RARRAY_PTR( RARRAY_PTR( sprite->command )[sprite->pc] );
            id = SYM2ID( com[0] );
            if( id == id_wait )
            {
                sprite->wait_count = NUM2INT( com[1] );
            }
            else if( id == id_add_dx )
            {
                sprite->dx += NUM2DBL( com[1] );
                sprite->speed_angle_link_flag = 0;
            }
            else if( id == id_add_dy )
            {
                sprite->dy += NUM2DBL( com[1] );
                sprite->speed_angle_link_flag = 0;
            }
            else if( id == id_add_alpha )
            {
                sprite->image_alpha += NUM2INT( com[1] );
            }
            else if( id == id_accel )
            {
                if( sprite->speed_angle_link_flag == 0 )
                {
                    sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
                    sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
                }
                sprite->speed += NUM2DBL( com[1] );
                sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
                sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
                sprite->speed_angle_link_flag = 1;
            }
            else if( id == id_rotate )
            {
                if( sprite->speed_angle_link_flag == 0 )
                {
                    sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
                    sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
                }
                sprite->angle += NUM2DBL( com[1] );
                sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
                sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
                sprite->speed_angle_link_flag = 1;
            }
            else if( id == id_rotate_image )
            {
                sprite->image_angle += NUM2DBL( com[1] );
                sprite->image_sync_flag = 0;
            }
            else if( id == id_set_x )
            {
                sprite->x = NUM2INT( com[1] );
            }
            else if( id == id_set_y )
            {
                sprite->y = NUM2INT( com[1] );
            }
            else if( id == id_set_dx )
            {
                sprite->dx = NUM2DBL( com[1] );
                sprite->speed_angle_link_flag = 0;
            }
            else if( id == id_set_dy )
            {
                sprite->dy = NUM2DBL( com[1] );
                sprite->speed_angle_link_flag = 0;
            }
            else if( id == id_set_speed )
            {
                if( sprite->speed_angle_link_flag == 0 )
                {
                    sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
                }
                sprite->speed = NUM2DBL( com[1] );
                sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
                sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
                sprite->speed_angle_link_flag = 1;
            }
            else if( id == id_set_angle )
            {
                if( sprite->speed_angle_link_flag == 0 )
                {
                    sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
                }
                sprite->angle = NUM2DBL( com[1] );
                sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
                sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
                sprite->speed_angle_link_flag = 1;
            }
            else if( id == id_delete )
            {
                sprite->delete_flag = 1;
                return self;
            }
            else
            {
                rb_funcall2( self, id, RARRAY_LEN( RARRAY_PTR( sprite->command )[sprite->pc] ) - 1, &com[1] );
            }
        }
        sprite->wait_count--;
    }

    /* アニメ処理 */
    if( sprite->anime_flag != 0 )
    {
        VALUE *current = RARRAY_PTR( sprite->current_anime );
        Check_Type( sprite->anime_image, T_ARRAY );
        sprite->anime_count++;

        if( sprite->anime_count >= RARRAY_LEN( current[1] == Qnil ? sprite->anime_image : current[1] ) * NUM2INT( current[0] ) )
        {
            sprite->anime_count = 0;

            if( current[2] != Qnil )
            {
                VALUE varray;
                varray = hash_lookup( sprite->anime_hash, current[2] );
                if( varray == Qnil )
                {
                    rb_funcall2( self, SYM2ID( current[2] ), 0, 0 );
                    if( sprite->delete_flag != 0 )
                    {
                        return Qnil;
                    }
                }
                else
                {
                    sprite->current_anime = varray;
                }
            }
        }

        if( current[1] == Qnil )
        {
            if( RARRAY_LEN( sprite->anime_image ) > 0 )
            {
                sprite->image = RARRAY_PTR( sprite->anime_image )[sprite->anime_count / NUM2INT( current[0] )];
            }
            else
            {
                sprite->image = Qnil;
            }
        }
        else
        {
            if( RARRAY_LEN( sprite->anime_image ) > 0 && RARRAY_LEN( current[1]) )
            {
                sprite->image = RARRAY_PTR( sprite->anime_image )[NUM2INT( RARRAY_PTR( current[1] )[ sprite->anime_count / NUM2INT( current[0] )] )];
            }
            else
            {
                sprite->image = Qnil;
            }
        }
    }

    /* 自動回転 */
    sprite->image_angle += sprite->image_turn;

    sprite->x += sprite->dx;
    sprite->y += sprite->dy;

    /* 画面外チェック（できれば画像のサイズを考慮したい？） */
    if( sprite->check_range_flag != 0 &&
        ((int)sprite->x < sprite->range_x1 ||
         (int)sprite->x > sprite->range_x2 ||
         (int)sprite->y < sprite->range_y1 ||
         (int)sprite->y > sprite->range_y2) )
    {
//        if( sprite->out_del_flag != 0 )
//        {
//            sprite->delete_flag = 1;        /* 自動delete */
//            return Qnil;
//        }
//        else
//        {
            rb_funcall( self, id_out, 0 );   /* 画面外でout呼び出し */
            if( sprite->delete_flag != 0 )  /* delete_flagがtrueになって帰ってきたらそこで処理終わり */
            {
                return Qnil;
            }
//        }
    }

    if( sprite->image_sync_flag != 0 )   /* 角度を同期する */
    {
        if( sprite->speed_angle_link_flag == 0 )
        {
            sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
        }
        sprite->image_angle = sprite->angle + sprite->image_difference;
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   内部の描画
 ---------------------------------------------------------------------*/
static VALUE Sprite_internal_draw( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( sprite->image != Qnil && sprite->visible_flag != 0 && sprite->delete_flag == 0)
    {
        DXRuby_drawEx( sprite->x + g_dx, sprite->y + g_dy, sprite->image, sprite->image_angle,
                       sprite->image_scalex, sprite->image_scaley, 0, 0, sprite->image_alpha, sprite->blend, sprite->z, 0 );
    }

    return self;
}


/*--------------------------------------------------------------------
   アニメーション開始
 ---------------------------------------------------------------------*/
static VALUE Sprite_start_animation( VALUE argc, VALUE *argv, VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    int i;
    VALUE varg, vpattern, vnext;

    rb_scan_args( argc, argv, "12", &varg, &vpattern, &vnext );

    if( TYPE( varg ) == T_SYMBOL )
    {
        VALUE temp = hash_lookup( sprite->anime_hash, varg );
        if( temp == Qnil )
        {
            return self;
        }
        sprite->current_anime = temp;
    }
    else if( FIXNUM_P( varg ) )
    {
        if( vpattern != Qnil ) Check_Type( vpattern, T_ARRAY );
        if( vnext != Qnil )    Check_Type( vnext, T_SYMBOL );
        sprite->current_anime = rb_ary_new();
        rb_ary_push( sprite->current_anime, varg );
        rb_ary_push( sprite->current_anime, vpattern );
        rb_ary_push( sprite->current_anime, vnext );
    }
    else
    {
        rb_raise(rb_eTypeError, "wrong argument type %s (expected Symbol or Numeric)", rb_obj_classname( varg ));
    }

    sprite->anime_count = 0;
    sprite->anime_flag = 1;

    return self;
}

/*--------------------------------------------------------------------
   アニメーション変更
 ---------------------------------------------------------------------*/
static VALUE Sprite_change_animation( VALUE argc, VALUE *argv, VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    int i;
    VALUE varg, vpattern, vnext;

    rb_scan_args( argc, argv, "12", &varg, &vpattern, &vnext );

    if( TYPE( varg ) == T_SYMBOL )
    {
        VALUE temp = hash_lookup( sprite->anime_hash, varg );
        if( temp == Qnil )
        {
            return self;
        }
        sprite->current_anime = temp;
    }
    else if( FIXNUM_P( varg ) )
    {
        if( vpattern != Qnil ) Check_Type( vpattern, T_ARRAY );
        if( vnext != Qnil )    Check_Type( vnext, T_SYMBOL );
        sprite->current_anime = rb_ary_new();
        rb_ary_push( sprite->current_anime, varg );
        rb_ary_push( sprite->current_anime, vpattern );
        rb_ary_push( sprite->current_anime, vnext );
    }
    else
    {
        rb_raise(rb_eTypeError, "wrong argument type %s (expected Symbol or Numeric)", rb_obj_classname( varg ));
    }

    return self;
}

/*--------------------------------------------------------------------
   アニメーション一時停止
 ---------------------------------------------------------------------*/
static VALUE Sprite_pause_animation( VALUE self )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->anime_flag = 0;
    return self;
}

/*--------------------------------------------------------------------
   アニメーション再開
 ---------------------------------------------------------------------*/
static VALUE Sprite_resume_animation( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    if( sprite->current_anime != Qnil ) sprite->anime_flag = 1;
    return self;
}

/*--------------------------------------------------------------------
   セッターとゲッター
 ---------------------------------------------------------------------*/

/* range */
static VALUE Sprite_get_range( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    if( sprite->check_range_flag == 0 )
    {
        return Qnil;
    }
    return rb_ary_new3( 4, rb_float_new( sprite->range_x1 )
                         , rb_float_new( sprite->range_y1 )
                         , rb_float_new( sprite->range_x2 )
                         , rb_float_new( sprite->range_y2 ) );
}

/* range= */
static VALUE Sprite_set_range( VALUE self, VALUE vrange )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( vrange == Qnil )
    {
        sprite->check_range_flag = 0;
        return Qnil;
    }

    Check_Type( vrange, T_ARRAY );

    if( RARRAY_LEN( vrange ) < 4 )
    {
        rb_raise( eDXRubyFwError, "要素数が少なすぎます" );
    }

    sprite->check_range_flag = 1;
    sprite->range_x1 = NUM2INT( RARRAY_PTR( vrange )[0] );
    sprite->range_y1 = NUM2INT( RARRAY_PTR( vrange )[1] );
    sprite->range_x2 = NUM2INT( RARRAY_PTR( vrange )[2] );
    sprite->range_y2 = NUM2INT( RARRAY_PTR( vrange )[3] );

    return vrange;
}

/* x */
static VALUE Sprite_get_x( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->x );
}

/* x= */
static VALUE Sprite_set_x( VALUE self, VALUE vx )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->x = (float)NUM2DBL( vx );
    return vx;
}

/* y */
static VALUE Sprite_get_y( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->y );
}

/* y= */
static VALUE Sprite_set_y( VALUE self, VALUE vy )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->y = (float)NUM2DBL( vy );
    return vy;
}

/* z */
static VALUE Sprite_get_z( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->z );
}

/* z= */
static VALUE Sprite_set_z( VALUE self, VALUE vz )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->z = (float)NUM2DBL( vz );
    return vz;
}

/* x_i */
static VALUE Sprite_get_xi( VALUE self )
{
    return INT2FIX( (int)(DXRUBYFW_GET_STRUCT( Sprite, self )->x) );
}

/* y_i */
static VALUE Sprite_get_yi( VALUE self )
{
    return INT2FIX( (int)(DXRUBYFW_GET_STRUCT( Sprite, self )->y) );
}

/* dx */
static VALUE Sprite_get_dx( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->dx );
}

/* dx= */
static VALUE Sprite_set_dx( VALUE self, VALUE vdx )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    sprite->dx = (float)NUM2DBL( vdx );
    sprite->speed_angle_link_flag = 0;
    return vdx;
}

/* dy */
static VALUE Sprite_get_dy( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->dy );
}

/* dy= */
static VALUE Sprite_set_dy( VALUE self, VALUE vdy )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    sprite->dy = (float)NUM2DBL( vdy );
    sprite->speed_angle_link_flag = 0;
    return vdy;
}

/* speed */
static VALUE Sprite_get_speed( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( sprite->speed_angle_link_flag == 0 )
    {
        sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
        sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
        sprite->speed_angle_link_flag = 1;
    }

    return rb_float_new( sprite->speed );
}

/* speed= */
static VALUE Sprite_set_speed( VALUE self, VALUE vspeed )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    if( sprite->speed_angle_link_flag == 0 )
    {
        sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
    }
    sprite->speed = NUM2DBL( vspeed );
    sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
    sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
    sprite->speed_angle_link_flag = 1;
    return vspeed;
}

/* angle */
static VALUE Sprite_get_angle( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( sprite->speed_angle_link_flag == 0 )
    {
        sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
        sprite->angle = atan2( sprite->dy, sprite->dx ) / PI * 180;
        sprite->speed_angle_link_flag = 1;
    }

    return rb_float_new( sprite->angle );
}

/* angle= */
static VALUE Sprite_set_angle( VALUE self, VALUE vangle )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    if( sprite->speed_angle_link_flag == 0 )
    {
        sprite->speed = sqrt( sprite->dx * sprite->dx + sprite->dy * sprite->dy );
    }
    sprite->angle = NUM2DBL( vangle );
    sprite->dx = cos(sprite->angle / 180 * PI) * sprite->speed;
    sprite->dy = sin(sprite->angle / 180 * PI) * sprite->speed;
    sprite->speed_angle_link_flag = 1;
    return vangle;
}



/* delete? */
static VALUE Sprite_get_delete_flag( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->delete_flag != 0 ? Qtrue : Qfalse;
}

/* delete */
static VALUE Sprite_delete( VALUE self )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->delete_flag = 1;
    return self;
}

/* visible */
static VALUE Sprite_get_visible_flag( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->visible_flag != 0 ? Qtrue : Qfalse;
}

/* visible= */
static VALUE Sprite_set_visible_flag( VALUE self, VALUE vflag )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->visible_flag = RTEST( vflag );
    return vflag;
}

/* image_turn */
static VALUE Sprite_get_image_turn( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->image_turn );
}

/* image_turn= */
static VALUE Sprite_set_image_turn( VALUE self, VALUE vturn )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_turn = (float)NUM2DBL( vturn );
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_sync_flag = 0;
    return vturn;
}

/* image_difference */
static VALUE Sprite_get_image_difference( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->image_difference );
}

/* image_difference= */
static VALUE Sprite_set_image_difference( VALUE self, VALUE vturn )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_difference = (float)NUM2DBL( vturn );
    return vturn;
}

/* image_sync */
static VALUE Sprite_get_image_sync( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->image_sync_flag != 0 ? Qtrue : Qfalse;
}

/* image_sync= */
static VALUE Sprite_set_image_sync( VALUE self, VALUE vflag )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_sync_flag = RTEST( vflag );
    return vflag;
}

/* image */
static VALUE Sprite_get_image( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->image;
}

/* image= */
static VALUE Sprite_set_image( VALUE self, VALUE vimage )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image = vimage;
    return vimage;
}

/* blend */
static VALUE Sprite_get_blend( VALUE self )
{
    return INT2FIX( DXRUBYFW_GET_STRUCT( Sprite, self )->blend );
}

/* blend= */
static VALUE Sprite_set_blend( VALUE self, VALUE vblend )
{
    int blend = NUM2INT( vblend );
    
    if( blend != 0 && blend != 4 && blend != 5 )
    {
        rb_raise( eDXRubyFwError, "値が異常です" );
    }

    DXRUBYFW_GET_STRUCT( Sprite, self )->blend = blend;
    return vblend;
}

/* collision */
static VALUE Sprite_get_collision( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( sprite->use_collision_flag == 0 )
    {
        return Qnil;
    }

    return rb_ary_new3( 4, INT2FIX( sprite->base_collision_x1 )
                         , INT2FIX( sprite->base_collision_y1 )
                         , INT2FIX( sprite->base_collision_x2 )
                         , INT2FIX( sprite->base_collision_y2 ) );
}

/* collision= */
static VALUE Sprite_set_collision( VALUE self, VALUE vcollision )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );

    if( vcollision == Qnil )
    {
        sprite->use_collision_flag = 0;
        return Qnil;
    }

    Check_Type( vcollision, T_ARRAY );

    if( RARRAY_LEN( vcollision ) < 4 )
    {
        rb_raise( eDXRubyFwError, "要素数が少なすぎます" );
    }

    sprite->use_collision_flag = 1;
    sprite->base_collision_x1 = NUM2INT( RARRAY_PTR( vcollision )[0] );
    sprite->base_collision_y1 = NUM2INT( RARRAY_PTR( vcollision )[1] );
    sprite->base_collision_x2 = NUM2INT( RARRAY_PTR( vcollision )[2] );
    sprite->base_collision_y2 = NUM2INT( RARRAY_PTR( vcollision )[3] );

    return vcollision;
}

/* collision_enable */
static VALUE Sprite_get_collision_enable_flag( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->collision_enable_flag != 0 ? Qtrue : Qfalse;
}

/* collision_enable= */
static VALUE Sprite_set_collision_enable_flag( VALUE self, VALUE vflag )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->collision_enable_flag = RTEST( vflag );;
    return vflag;
}

/* image_angle */
static VALUE Sprite_get_image_angle( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->image_angle );
}

/* image_angle= */
static VALUE Sprite_set_image_angle( VALUE self, VALUE vangle )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_angle = NUM2DBL( vangle );
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_sync_flag = 0;
    return vangle;
}

/* image_scalex */
static VALUE Sprite_get_image_scalex( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->image_scalex );
}

/* image_scalex= */
static VALUE Sprite_set_image_scalex( VALUE self, VALUE vscalex )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_scalex = NUM2DBL( vscalex );
    return vscalex;
}

/* image_scaley */
static VALUE Sprite_get_image_scaley( VALUE self )
{
    return rb_float_new( DXRUBYFW_GET_STRUCT( Sprite, self )->image_scaley );
}

/* image_scaley= */
static VALUE Sprite_set_image_scaley( VALUE self, VALUE vscaley )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_scaley = NUM2DBL( vscaley );
    return vscaley;
}

/* image_alpha */
static VALUE Sprite_get_image_alpha( VALUE self )
{
    return INT2FIX( DXRUBYFW_GET_STRUCT( Sprite, self )->image_alpha );
}

/* image_alpha= */
static VALUE Sprite_set_image_alpha( VALUE self, VALUE valpha )
{
    int alpha = NUM2INT( valpha );

    if( alpha < 0 || alpha > 255 )
    {
        rb_raise( eDXRubyFwError, "値が範囲外です" );
    }
    DXRUBYFW_GET_STRUCT( Sprite, self )->image_alpha = alpha;
    return valpha;
}

/* call_draw_flag */
static VALUE Sprite_get_call_draw_flag( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->call_draw_flag != 0 ? Qtrue : Qfalse;
}

/* call_draw_flag= */
static VALUE Sprite_set_call_draw_flag( VALUE self, VALUE vcalldraw )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->call_draw_flag = RTEST( vcalldraw );
    return vcalldraw;
}

/* call_update_flag */
static VALUE Sprite_get_call_update_flag( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->call_update_flag != 0 ? Qtrue : Qfalse;
}

/* call_update_flag= */
static VALUE Sprite_set_call_update_flag( VALUE self, VALUE vcallupdate )
{
    DXRUBYFW_GET_STRUCT( Sprite, self )->call_update_flag = RTEST( vcallupdate );
    return vcallupdate;
}

/* add_animation */
static VALUE Sprite_add_animation( VALUE argc, VALUE *argv, VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    VALUE vsym, vframe, vpattern, vnext, temp;

    rb_scan_args( argc, argv, "22", &vsym, &vframe, &vpattern, &vnext );

    Check_Type( vsym, T_SYMBOL );
    NUM2INT( vframe ); // 型チェック

    if( sprite->anime_hash == Qnil ) sprite->anime_hash = rb_hash_new();
    if( vpattern != Qnil )           Check_Type( vpattern, T_ARRAY );
    if( vnext != Qnil )              Check_Type( vnext, T_SYMBOL );

    temp = rb_ary_new();

    rb_ary_push( temp, vframe );
    rb_ary_push( temp, vpattern );
    rb_ary_push( temp, vnext );

    rb_hash_aset( sprite->anime_hash, vsym, temp );
    return self;
}

/* anime_image */
static VALUE Sprite_get_anime_image( VALUE self )
{
    return DXRUBYFW_GET_STRUCT( Sprite, self )->anime_image;
}

/* anime_image= */
static VALUE Sprite_set_anime_image( VALUE self, VALUE vanime_image )
{
    Check_Type( vanime_image, T_ARRAY );
    DXRUBYFW_GET_STRUCT( Sprite, self )->anime_image = vanime_image;
    return vanime_image;
}

/* start_command */
static VALUE Sprite_start_command( VALUE self, VALUE vcommand )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    int i;

    if( vcommand == Qnil )
    {
        sprite->command = Qnil;
        sprite->wait_count = 1;
        sprite->pc = -1;
        return vcommand;
    }

    Check_Type( vcommand, T_ARRAY );
    for( i = 0; i < RARRAY_LEN( vcommand ); i++ )
    {
        VALUE com = RARRAY_PTR( vcommand )[i];
        VALUE sym;
        ID id;

        Check_Type( com, T_ARRAY );
        sym = RARRAY_PTR( com )[0];
        Check_Type( sym, T_SYMBOL );
        id = SYM2ID( sym );
        if( id == id_wait )
        {
            break;
        }
    }
    if( i == RARRAY_LEN( vcommand ) )
    {
        rb_raise( eDXRubyFwError, "not exist 'wait' command" );
    }
    sprite->command = vcommand;
    sprite->wait_count = 0;
    sprite->pc = -1;
//    rb_ary_clear( sprite->stack );

    return vcommand;
}

/* param_hash */
static VALUE Sprite_get_param_hash( VALUE self )
{
    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
    VALUE vresult;

    vresult = rb_hash_new();
    rb_hash_aset( vresult, symbol_angle, rb_float_new(sprite->image_angle) );
    rb_hash_aset( vresult, symbol_alpha, INT2FIX(sprite->image_alpha) );
    rb_hash_aset( vresult, symbol_scalex, rb_float_new(sprite->image_scalex) );
    rb_hash_aset( vresult, symbol_scaley, rb_float_new(sprite->image_scaley) );
    rb_hash_aset( vresult, symbol_z, rb_float_new(sprite->z) );

    switch( sprite->blend )
    {
    case 0:
        rb_hash_aset( vresult, symbol_blend, symbol_alpha );
        break;
    case 4:
        rb_hash_aset( vresult, symbol_blend, symbol_add );
        break;
    case 5:
        rb_hash_aset( vresult, symbol_blend, symbol_add2 );
        break;
    default:
        break;
    }

    return vresult;
}


/* hit? */
//static VALUE Sprite_check_hit( VALUE self, VALUE target )
//{
//    struct DXRubyFwSprite *sprite = DXRUBYFW_GET_STRUCT( Sprite, self );
//    struct DXRubyFwSprite *tsprite;
//    DXRUBYFW_CHECK_TYPE( Sprite, target );
//
//    tsprite = DXRUBYFW_GET_STRUCT( Sprite, target );
//            if( data1->collision_x1 <= data2->collision_x2 && data1->collision_y1 <= data2->collision_y2 &&
//                data2->collision_x1 <= data1->collision_x2 && data2->collision_y1 <= data1->collision_y2 )
//                rb_funcall( RARRAY_PTR(d)[j], hit, 1, RARRAY_PTR(o)[i] );
//}




/*--------------------------------------------------------------------
   dummy
 ---------------------------------------------------------------------*/
static VALUE Sprite_dummy( VALUE klass, VALUE obj )
{
    return obj;
}



/*--------------------------------------------------------------------
   クラスメソッド群
 ---------------------------------------------------------------------*/
/* global_dx */
static VALUE Sprite_get_global_dx( VALUE klass )
{
    return rb_float_new( g_dx );
}

/* global_dx= */
static VALUE Sprite_set_global_dx( VALUE klass, VALUE vx )
{
    g_dx = (float)NUM2DBL( vx );
    return vx;
}

/* global_dy */
static VALUE Sprite_get_global_dy( VALUE klass )
{
    return rb_float_new( g_dy );
}

/* global_dy= */
static VALUE Sprite_set_global_dy( VALUE klass, VALUE vy )
{
    g_dy = (float)NUM2DBL( vy );
    return vy;
}

static VALUE Sprite_class_update( VALUE klass, VALUE obj )
{
    int i;

    if( TYPE( obj ) != T_ARRAY )
    {
        obj = rb_ary_new3( 1, obj );
    }

    for( i = 0; i < RARRAY_LEN( obj ); i++ )
    {
        VALUE p = RARRAY_PTR( obj )[i];

        if( TYPE( p ) != T_DATA || RDATA( p )->dfree != (RUBY_DATA_FUNC)Sprite_release ||
            (DXRUBYFW_GET_STRUCT( Sprite, p )->call_update_flag != 0 && DXRUBYFW_GET_STRUCT( Sprite, p )->delete_flag == 0) )
        {
            if( rb_obj_respond_to( p, id_update, Qfalse ) )
            {
                rb_funcall2( p, id_update, 0, 0 );
            }
            continue;
        }

        /* 削除済みのオブジェクトは無視 */
        if( DXRUBYFW_GET_STRUCT( Sprite, p )->delete_flag != 0 )
        {
            continue;
        }

        Sprite_internal_update( p );
    }

    return Qnil;
}


static VALUE Sprite_class_draw( VALUE klass, VALUE obj )
{
    int i;

    if( TYPE( obj ) != T_ARRAY )
    {
        DXRUBYFW_CHECK_TYPE( Sprite, obj );

        /* 削除済みのオブジェクトは無視 */
        if( DXRUBYFW_GET_STRUCT( Sprite, obj )->delete_flag != 0 )
        {
            return Qnil;
        }

        Sprite_internal_draw( obj );
        return Qnil;
    }

    for( i = 0; i < RARRAY_LEN( obj ); i++ )
    {
        VALUE p = RARRAY_PTR( obj )[i];

        /* Spriteオブジェクト以外なら無視 */
        if( TYPE( p ) != T_DATA || RDATA( p )->dfree != (RUBY_DATA_FUNC)Sprite_release ||
            (DXRUBYFW_GET_STRUCT( Sprite, p )->call_draw_flag != 0 && DXRUBYFW_GET_STRUCT( Sprite, p )->delete_flag == 0) )
        {
            if( rb_obj_respond_to( p, id_draw, Qfalse ) )
            {
                rb_funcall2( p, id_draw, 0, 0 );
            }
            continue;
        }

        /* 削除済みのオブジェクトは無視 */
        if( DXRUBYFW_GET_STRUCT( Sprite, p )->delete_flag != 0 )
        {
            continue;
        }

        Sprite_internal_draw( p );
    }

    return Qnil;
}


static VALUE Sprite_class_clean( VALUE klass, VALUE obj )
{
    int i;
    struct DXRubyFwSprite *sprite;

    Check_Type( obj, T_ARRAY );

    for( i = 0; i < RARRAY_LEN( obj ); i++ )
    {
        VALUE p = RARRAY_PTR( obj )[i];

        /* Spriteオブジェクト以外ならdeleted?を呼んでみる */
        if( TYPE( p ) != T_DATA || RDATA( p )->dfree != (RUBY_DATA_FUNC)Sprite_release )
        {
            if( rb_obj_respond_to( p, id_delete_flag, Qfalse ) )
            {
                if( RTEST( rb_funcall2( p, id_delete_flag, 0, 0 ) ) )
                {
                    RARRAY_PTR( obj )[i] = Qnil;
                }
            }
            continue;
        }

        /* 削除済みのオブジェクトを配列から削除 */
        if( DXRUBYFW_GET_STRUCT( Sprite, p )->delete_flag != 0 )
        {
            RARRAY_PTR( obj )[i] = Qnil;
        }
    }

    rb_funcall( obj, rb_intern("compact!"), 0 );

    return Qnil;
}


static VALUE Sprite_class_check( VALUE argc, VALUE *argv, VALUE self )
{
    int i, j;
    struct DXRubyFwSprite *data1;
    struct DXRubyFwSprite *data2;
    int flag = 0;           /* 別グループなら0、同じグループなら1 */
    VALUE hitflag = Qfalse; /* 一つでも当たったらQtrue */
    VALUE o, d;
    ID shot, hit;

    rb_scan_args( argc, argv, "22", &o, &d, &shot, &hit );
    if( shot == Qnil )
    {
        shot = id_shot;
    }
    else
    {
        shot = SYM2ID(shot);
    }

    if( hit == Qnil )
    {
        hit = id_hit;
    }
    else
    {
        hit = SYM2ID(hit);
    }

    if( TYPE(o) != T_ARRAY )
    {
        o = rb_ary_new3( 1, o );
    }
    if( TYPE(d) != T_ARRAY )
    {
        d = rb_ary_new3( 1, d );
    }

    /* 衝突判定範囲設定 */
    for( i = 0; i < RARRAY_LEN(o); i++ )
    {
        struct DXRubyFwSprite *sprite;

        /* Spriteオブジェクト以外ならなにもしない */
        if( TYPE( RARRAY_PTR(o)[i] ) != T_DATA || RDATA( RARRAY_PTR(o)[i] )->dfree != (RUBY_DATA_FUNC)Sprite_release )
        {
            continue;
        }

        sprite = DXRUBYFW_GET_STRUCT( Sprite, RARRAY_PTR(o)[i] );

        if( sprite->use_collision_flag != 0 )
        {
            sprite->collision_x1 = sprite->x + sprite->base_collision_x1;
            sprite->collision_y1 = sprite->y + sprite->base_collision_y1;
            sprite->collision_x2 = sprite->x + sprite->base_collision_x2;
            sprite->collision_y2 = sprite->y + sprite->base_collision_y2;
        }
    }

    if( o == d )
    {
        flag = 1;
    }
    else
    {
        for( i = 0; i < RARRAY_LEN(d); i++ )
        {
            struct DXRubyFwSprite *sprite;

            /* Spriteオブジェクト以外ならなにもしない */
            if( TYPE( RARRAY_PTR(d)[i] ) != T_DATA || RDATA( RARRAY_PTR(d)[i] )->dfree != (RUBY_DATA_FUNC)Sprite_release )
            {
                continue;
            }

            sprite = DXRUBYFW_GET_STRUCT( Sprite, RARRAY_PTR(d)[i] );

            if( sprite->use_collision_flag != 0 )
            {
                sprite->collision_x1 = sprite->x + sprite->base_collision_x1;
                sprite->collision_y1 = sprite->y + sprite->base_collision_y1;
                sprite->collision_x2 = sprite->x + sprite->base_collision_x2;
                sprite->collision_y2 = sprite->y + sprite->base_collision_y2;
            }
        }
    }

    /* 攻撃側オブジェクトのループ */
    for( i = 0; i < RARRAY_LEN(o) - flag; i++ )
    {
        /* Spriteオブジェクト以外ならなにもしない */
        if( TYPE( RARRAY_PTR(o)[i] ) != T_DATA || RDATA( RARRAY_PTR(o)[i] )->dfree != (RUBY_DATA_FUNC)Sprite_release )
        {
            continue;
        }

        /* 配列からデータ取得 */
        data1 = DXRUBYFW_GET_STRUCT( Sprite, RARRAY_PTR(o)[i] );

        /* 既に消えているか衝突判定が無効なら何もしない */
        if( data1->delete_flag != 0 || data1->use_collision_flag == 0 || data1->collision_enable_flag == 0 )
        {
            continue;
        }

        /* 防御側オブジェクトのループ */
        for( j = (flag == 0 ? 0 : i + 1); j < RARRAY_LEN(d); j++ )
        {
            /* Spriteオブジェクト以外ならなにもしない */
            if( TYPE( RARRAY_PTR(d)[j] ) != T_DATA || RDATA( RARRAY_PTR(d)[j] )->dfree != (RUBY_DATA_FUNC)Sprite_release )
            {
                continue;
            }

            /* 配列からデータ取得 */
            data2 = DXRUBYFW_GET_STRUCT( Sprite, RARRAY_PTR(d)[j] );

            /* 既に消えているか衝突判定が無効なら何もしない */
            if( data2->delete_flag != 0 || data2->use_collision_flag == 0 || data2->collision_enable_flag == 0 )
            {
                continue;
            }

            /* 判定 */
            if( data1->collision_x1 <= data2->collision_x2 && data1->collision_y1 <= data2->collision_y2 &&
                data2->collision_x1 <= data1->collision_x2 && data2->collision_y1 <= data1->collision_y2 )
            {
                hitflag = Qtrue;

                rb_funcall( RARRAY_PTR(d)[j], hit, 1, RARRAY_PTR(o)[i] );

                if( flag == 0 ) /* 別グループの場合 */
                {
                    /* 攻撃側が消えたらそれ以降の防御側ループ省略 */
                    rb_funcall( RARRAY_PTR(o)[i], shot, 1, RARRAY_PTR(d)[j] );
                    if( data1->delete_flag != 0 )
                    {
                        break;
                    }
                }
                else            /* 同じグループの場合 */
                {
                    /* 攻撃側が消えたらそれ以降の防御側ループ省略 */
                    rb_funcall( RARRAY_PTR(o)[i], hit, 1, RARRAY_PTR(d)[j] );
                    if( data1->delete_flag != 0 )
                    {
                        break;
                    }
                }
            }
        }
    }

    return hitflag;
}






/*********************************************************************
 * Matrixクラス
 *
 * 行列を表現する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Matrix_release( struct DXRubyFwMatrix* mat )
{
    mat->x = 1; /* 最適化抑止 */
    free( mat );
}

/*--------------------------------------------------------------------
   Matrixクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Matrix_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFwMatrix *mat;

    /* DXRubyFwMatrixのメモリ取得＆Matrixオブジェクト生成 */
    mat = malloc( sizeof( struct DXRubyFwMatrix ) );
    if( mat == NULL ) rb_raise( eDXRubyFwError, "メモリの取得に失敗しました - Matrix_allocate" );
    obj = Data_Wrap_Struct( klass, 0, Matrix_release, mat );

    mat->x = 0;
    mat->y = 0;
    ZeroMemory( mat->m, sizeof( float ) * 16 );

    return obj;
}

/*--------------------------------------------------------------------
   MatrixクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Matrix_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyFwMatrix *mat = DXRUBYFW_GET_STRUCT( Matrix, self );
    VALUE ary, *ary_p;
    int i, j, count;

    if( argc == 0 )
    {
        mat->m11 = 1;mat->m22 = 1;mat->m33 = 1;
        mat->x = mat->y = 3;
        return self;
    }

    if( argc == 1 )
    {
        ary = argv[0];
        Check_Type( ary, T_ARRAY );
        if( RARRAY_LEN( ary ) > 4 || RARRAY_LEN( ary ) < 1 ) rb_raise( eDXRubyFwError, "配列の数が正しくありません。 - Matrix_initialize");
        ary_p = RARRAY_PTR( ary );
        count = RARRAY_LEN( ary );
    }
    else
    {
        if( argc > 4 ) rb_raise( eDXRubyFwError, "引数の数が正しくありません。 - Matrix_initialize");
        ary_p = argv;
        count = argc;
    }

    mat->y = count;
    for( i = 0; i < count; i++ )
    {
        VALUE ary2 = ary_p[i];
        Check_Type( ary2, T_ARRAY );
        if( RARRAY_LEN( ary2 ) > 4 || RARRAY_LEN( ary2 ) < 1 || RARRAY_LEN( ary2 ) != mat->y ) rb_raise( eDXRubyFwError, "配列の数が正しくありません。 - Matrix_initialize");

        for( j = 0; j < RARRAY_LEN( ary2 ); j++ )
        {
            mat->m[i][j] = NUM2DBL( RARRAY_PTR( ary2 )[j] );
        }
    }
    mat->x = RARRAY_LEN( ary_p[0] );

    return self;
}

static VALUE Matrix_mul( VALUE self, VALUE varg )
{
    struct DXRubyFwMatrix *mat_d = DXRUBYFW_GET_STRUCT( Matrix, self );
    struct DXRubyFwMatrix *result;
    VALUE vresult;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        int i, j;

        vresult = Matrix_allocate( cMatrix );
        result = DXRUBYFW_GET_STRUCT( Matrix, vresult );
        result->x = mat_d->x;
        result->y = mat_d->y;
        for( i = 0; i < mat_d->y; i++ )
        {
            for( j = 0; j < mat_d->x; j++ )
            {
                result->m[i][j] = mat_d->m[i][j] * NUM2DBL( varg );
            }
        }
    }
    else
    {
        int i, j, k;
        struct DXRubyFwMatrix *mat_s;

        DXRUBYFW_CHECK_TYPE( Matrix, varg );
        mat_s = DXRUBYFW_GET_STRUCT( Matrix, varg );

        if( mat_d->x != mat_s->y || mat_d->y != mat_s->x ) rb_raise( eDXRubyFwError, "要素数が一致していません。 - Matrix_*");
        vresult = Matrix_allocate( cMatrix );
        result = DXRUBYFW_GET_STRUCT( Matrix, vresult );
        result->x = mat_s->x;
        result->y = mat_d->y;

        for( i = 0; i < mat_d->y; i++ )
        {
            for( j = 0; j < mat_s->x; j++ )
            {
                for( k = 0; k < mat_s->x; k++ )
                {
                    result->m[i][j] += mat_d->m[i][k] * mat_s->m[k][j];
                }
            }
        }
    }
    return vresult;
}


static VALUE Matrix_to_s( VALUE self )
{
    struct DXRubyFwMatrix *mat = DXRUBYFW_GET_STRUCT( Matrix, self );
    char buf[256];
    sprintf( buf, "x = %d, y = %d, (%f, %f, %f, %f)(%f, %f, %f, %f)(%f, %f, %f, %f)(%f, %f, %f, %f)", mat->x, mat->y
        , mat->m11, mat->m12, mat->m13, mat->m14
        , mat->m21, mat->m22, mat->m23, mat->m24
        , mat->m31, mat->m32, mat->m33, mat->m34
        , mat->m41, mat->m42, mat->m43, mat->m44);
    return rb_str_new2( buf );
}

/* 回転行列作成(2D) */
static VALUE Matrix_create_rot( VALUE klass, VALUE vangle )
{
    struct DXRubyFwMatrix *result;
    VALUE vresult;
    float angle;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBYFW_GET_STRUCT( Matrix, vresult );

    result->x = 3;
    result->y = 3;
    angle = PI / 180.0f * NUM2DBL( vangle );
    result->m11 = cos( angle );
    result->m12 = sin( angle );
    result->m21 = -result->m12;
    result->m22 = result->m11;
    result->m33 = 1;

    return vresult;
}
/* 平行移動行列作成(2D) */
static VALUE Matrix_create_trans( VALUE klass, VALUE vx, VALUE vy )
{
    struct DXRubyFwMatrix *result;
    VALUE vresult;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBYFW_GET_STRUCT( Matrix, vresult );

    result->x = 3;
    result->y = 3;
    result->m11 = 1;
    result->m22 = 1;
    result->m31 = NUM2DBL( vx );
    result->m32 = NUM2DBL( vy );
    result->m33 = 1;

    return vresult;
}
/* スケーリング行列作成(2D) */
static VALUE Matrix_create_scale( VALUE klass, VALUE vx, VALUE vy )
{
    struct DXRubyFwMatrix *result;
    VALUE vresult;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBYFW_GET_STRUCT( Matrix, vresult );

    result->x = 1;
    result->y = 1;
    result->m11 = NUM2DBL( vx );
    result->m22 = NUM2DBL( vy );
    result->m33 = 1;

    return vresult;
}


/*********************************************************************
 * Vectorクラス
 *
 * ベクトルを表現する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Vector_release( struct DXRubyFwVector* vec )
{
    vec->x = 2; /* 最適化抑止 */
    free( vec );
}

/*--------------------------------------------------------------------
   Vectorクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Vector_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFwVector *vec;
    int i;

    /* DXRubyFwVectorのメモリ取得＆Vectorオブジェクト生成 */
    vec = malloc( sizeof( struct DXRubyFwVector ) );
    if( vec == NULL ) rb_raise( eDXRubyFwError, "メモリの取得に失敗しました - Vector_allocate" );
    obj = Data_Wrap_Struct( klass, 0, Vector_release, vec );

    vec->x = 0;
    ZeroMemory( vec->v, sizeof( float ) * 4 );

    return obj;
}

/*--------------------------------------------------------------------
   VectorクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Vector_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    VALUE ary;
    VALUE *ary_p;
    int i, count;

    if( argc == 0 )
    {
        return self;
    }

    if( argc == 1 )
    {
        ary = argv[0];
        Check_Type( ary, T_ARRAY );
        if( RARRAY_LEN( ary ) > 4 || RARRAY_LEN( ary ) < 1 ) rb_raise( eDXRubyFwError, "配列の数が正しくありません。 - Vector_initialize");
        ary_p = RARRAY_PTR( ary );
        count = RARRAY_LEN( ary );
    }
    else
    {
        if( argc > 4 ) rb_raise( eDXRubyFwError, "引数の数が正しくありません。 - Vector_initialize");
        ary_p = argv;
        count = argc;
    }

    vec->x = count;
    for( i = 0; i < count; i++ )
    {
        vec->v[i] = NUM2DBL( ary_p[i] );
    }

    return self;
}

static VALUE Vector_mul( VALUE self, VALUE varg )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *result;
    VALUE vresult;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        int i;
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec->x;

        for( i = 0; i < vec->x; i++ )
        {
            result->v[i] = vec->v[i] * NUM2DBL( varg );
        }
    }
    else if( TYPE( varg ) == T_DATA && RDATA( varg )->dfree == (RUBY_DATA_FUNC)Vector_release )
    {
        struct DXRubyFwVector *vec_s = DXRUBYFW_GET_STRUCT( Vector, varg );
        int i;
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec->x;

        for( i = 0; i < vec->x; i++ )
        {
            result->v[i] = vec->v[i] * vec_s->v[i];
        }
    }
    else if( TYPE( varg ) == T_DATA && RDATA( varg )->dfree == (RUBY_DATA_FUNC)Matrix_release )
    {
        struct DXRubyFwMatrix *mat;
        int i, j;
        DXRUBYFW_CHECK_TYPE( Matrix, varg );
        mat = DXRUBYFW_GET_STRUCT( Matrix, varg );

        if( vec->x != mat->y ) rb_raise( eDXRubyFwError, "要素数が一致していません。 - Vector_*");
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = mat->x;

        for( i = 0; i < mat->x; i++ )
        {
            for( j = 0; j < mat->y; j++)
            {
                result->v[i] += vec->v[j] * mat->m[j][i];
            }
        }
    }
    else
    {
        rb_raise( eDXRubyFwError, "引数が異常です");
    }

    return vresult;
}

static VALUE Vector_add( VALUE self, VALUE varg )
{
    struct DXRubyFwVector *vec_d = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *vec_s;
    struct DXRubyFwVector *result;
    VALUE vresult;
    int i;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] + NUM2DBL( varg );
        }
    }
    else
    {
        DXRUBYFW_CHECK_TYPE( Vector, varg );
        vec_s =  DXRUBYFW_GET_STRUCT( Vector, varg );
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] + vec_s->v[i];
        }
    }

    return vresult;
}

static VALUE Vector_sub( VALUE self, VALUE varg )
{
    struct DXRubyFwVector *vec_d = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *vec_s;
    struct DXRubyFwVector *result;
    VALUE vresult;
    int i;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] - NUM2DBL( varg );
        }
    }
    else
    {
        DXRUBYFW_CHECK_TYPE( Vector, varg );
        vec_s =  DXRUBYFW_GET_STRUCT( Vector, varg );
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] - vec_s->v[i];
        }
    }

    return vresult;
}

static VALUE Vector_div( VALUE self, VALUE varg )
{
    struct DXRubyFwVector *vec_d = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *vec_s;
    struct DXRubyFwVector *result;
    VALUE vresult;
    int i;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] / NUM2DBL( varg );
        }
    }
    else
    {
        DXRUBYFW_CHECK_TYPE( Vector, varg );
        vec_s =  DXRUBYFW_GET_STRUCT( Vector, varg );
        vresult = Vector_allocate( cVector );
        result = DXRUBYFW_GET_STRUCT( Vector, vresult );
        result->x = vec_d->x;

        for( i = 0; i < vec_d->x; i++ )
        {
            result->v[i] = vec_d->v[i] / vec_s->v[i];
        }
    }

    return vresult;
}

static VALUE Vector_minus( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *result;
    VALUE vresult;
    int i;

    vresult = Vector_allocate( cVector );
    result = DXRUBYFW_GET_STRUCT( Vector, vresult );
    result->x = vec->x;

    for( i = 0; i < vec->x; i++ )
    {
        result->v[i] = -vec->v[i];
    }

    return vresult;
}

static VALUE Vector_normalize( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    struct DXRubyFwVector *result;
    VALUE vresult;
    float magsq = vec->v1*vec->v1 + vec->v2*vec->v2 + vec->v3*vec->v3 + vec->v4*vec->v4;
    int i;

    vresult = Vector_allocate( cVector );
    result = DXRUBYFW_GET_STRUCT( Vector, vresult );
    result->x = vec->x;

    if( magsq > 0.0f )
    {
        float temp = 1.0f / sqrt( magsq );
        int i;
        for( i = 0; i < vec->x; i++ )
        {
            result->v[i] = temp * vec->v[i];
        }
    }

    return vresult;
}

static VALUE Vector_distance( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyFwVector *vec1;
    struct DXRubyFwVector *vec2;
    float dx, dy, dz, dw;

    DXRUBYFW_CHECK_TYPE( Vector, vvec1 );
    DXRUBYFW_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBYFW_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBYFW_GET_STRUCT( Vector, vvec2 );

    dx = vec1->v1 - vec2->v1;
    dy = vec1->v2 - vec2->v2;
    dz = vec1->v3 - vec2->v3;
    dw = vec1->v4 - vec2->v4;
    return rb_float_new( sqrt( dx*dx + dy*dy + dz*dz + dw*dw ) );
}

static VALUE Vector_cross_product( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyFwVector *vec1;
    struct DXRubyFwVector *vec2;
    struct DXRubyFwVector *result;
    VALUE vresult;

    DXRUBYFW_CHECK_TYPE( Vector, vvec1 );
    DXRUBYFW_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBYFW_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBYFW_GET_STRUCT( Vector, vvec2 );

    vresult = Vector_allocate( cVector );
    result = DXRUBYFW_GET_STRUCT( Vector, vresult );
    result->x = 3;

    result->v1 = vec1->v2*vec2->v3 - vec1->v3*vec2->v2;
    result->v2 = vec1->v3*vec2->v1 - vec1->v1*vec2->v3;
    result->v3 = vec1->v1*vec2->v2 - vec1->v2*vec2->v1;

    return vresult;
}

static VALUE Vector_inner_product( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyFwVector *vec1;
    struct DXRubyFwVector *vec2;
    DXRUBYFW_CHECK_TYPE( Vector, vvec1 );
    DXRUBYFW_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBYFW_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBYFW_GET_STRUCT( Vector, vvec2 );
    return rb_float_new( vec1->v1 * vec2->v1 + vec1->v2 * vec2->v2 + vec1->v3 * vec2->v3 + vec1->v4 * vec2->v4 );
}

static VALUE Vector_to_s( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    char buf[128];
    sprintf( buf, "size = %d (%f, %f, %f, %f)", vec->x, vec->v1, vec->v2, vec->v3, vec->v4 );
    return rb_str_new2( buf );
}

static VALUE Vector_get_x( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v1);
}
static VALUE Vector_get_y( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v2);
}
static VALUE Vector_get_z( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v3);
}
static VALUE Vector_get_w( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v4);
}
static VALUE Vector_get_size( VALUE self )
{
    struct DXRubyFwVector *vec = DXRUBYFW_GET_STRUCT( Vector, self );
    return rb_float_new(vec->x);
}


/*
***************************************************************
*
*         Global functions
*
***************************************************************/

void Init_dxrubyfw()
{
    VALUE cWindow;

    /* DXRubyFwモジュール登録 */
    mDXRubyFw = rb_define_module( "DXRubyFw" );

    /* Spriteクラス定義 */
    cSprite = rb_define_class_under( mDXRubyFw, "Sprite", rb_cObject );

    /* Windowモジュールに追加するメソッド */
    cWindow = rb_eval_string( "DXRuby::Window" );
    rb_define_singleton_method( cWindow, "drawSprite", Sprite_class_draw, 1 );
    rb_define_singleton_method( cWindow, "draw_sprite", Sprite_class_draw, 1 );

    /* クラスメソッド */
    rb_define_singleton_method( cSprite, "global_dx", Sprite_get_global_dx, 0 );
    rb_define_singleton_method( cSprite, "global_dy", Sprite_get_global_dy, 0 );
    rb_define_singleton_method( cSprite, "global_dx=", Sprite_set_global_dx, 1 );
    rb_define_singleton_method( cSprite, "global_dy=", Sprite_set_global_dy, 1 );
    rb_define_singleton_method( cSprite, "update", Sprite_class_update, 1 );
    rb_define_singleton_method( cSprite, "clean", Sprite_class_clean, 1 );
    rb_define_singleton_method( cSprite, "check", Sprite_class_check, -1 );
    rb_define_singleton_method( cSprite, "draw", Sprite_class_draw, 1 );

    /* インスタンスメソッド */
//    rb_define_private_method( cSprite, "initialize", Sprite_initialize, -1 );
    rb_define_method( cSprite, "range", Sprite_get_range, 0 );
    rb_define_method( cSprite, "range=", Sprite_set_range, 1 );
    rb_define_method( cSprite, "x", Sprite_get_x, 0 );
    rb_define_method( cSprite, "x=", Sprite_set_x, 1 );
    rb_define_method( cSprite, "y", Sprite_get_y, 0 );
    rb_define_method( cSprite, "y=", Sprite_set_y, 1 );
    rb_define_method( cSprite, "z", Sprite_get_z, 0 );
    rb_define_method( cSprite, "z=", Sprite_set_z, 1 );
    rb_define_method( cSprite, "x_i", Sprite_get_xi, 0 );
    rb_define_method( cSprite, "y_i", Sprite_get_yi, 0 );

    rb_define_method( cSprite, "dx", Sprite_get_dx, 0 );
    rb_define_method( cSprite, "dx=", Sprite_set_dx, 1 );
    rb_define_method( cSprite, "dy", Sprite_get_dy, 0 );
    rb_define_method( cSprite, "dy=", Sprite_set_dy, 1 );
    rb_define_method( cSprite, "speed", Sprite_get_speed, 0 );
    rb_define_method( cSprite, "speed=", Sprite_set_speed, 1 );
    rb_define_method( cSprite, "angle", Sprite_get_angle, 0 );
    rb_define_method( cSprite, "angle=", Sprite_set_angle, 1 );

    rb_define_method( cSprite, "deleted?", Sprite_get_delete_flag, 0 );
    rb_define_method( cSprite, "delete", Sprite_delete, 0 );
    rb_define_method( cSprite, "visible", Sprite_get_visible_flag, 0 );
    rb_define_method( cSprite, "visible=", Sprite_set_visible_flag, 1 );

    rb_define_method( cSprite, "image_sync", Sprite_get_image_sync, 0 );
    rb_define_method( cSprite, "image_sync=", Sprite_set_image_sync, 1 );
    rb_define_method( cSprite, "image_turn", Sprite_get_image_turn, 0 );
    rb_define_method( cSprite, "image_turn=", Sprite_set_image_turn, 1 );
    rb_define_method( cSprite, "image_difference", Sprite_get_image_difference, 0 );
    rb_define_method( cSprite, "image_difference=", Sprite_set_image_difference, 1 );
    rb_define_method( cSprite, "image", Sprite_get_image, 0 );
    rb_define_method( cSprite, "image=", Sprite_set_image, 1 );
    rb_define_method( cSprite, "image_angle", Sprite_get_image_angle, 0 );
    rb_define_method( cSprite, "image_angle=", Sprite_set_image_angle, 1 );
    rb_define_method( cSprite, "image_scale_x", Sprite_get_image_scalex, 0 );
    rb_define_method( cSprite, "image_scale_x=", Sprite_set_image_scalex, 1 );
    rb_define_method( cSprite, "image_scale_y", Sprite_get_image_scaley, 0 );
    rb_define_method( cSprite, "image_scale_y=", Sprite_set_image_scaley, 1 );
    rb_define_method( cSprite, "image_alpha", Sprite_get_image_alpha, 0 );
    rb_define_method( cSprite, "image_alpha=", Sprite_set_image_alpha, 1 );
    rb_define_method( cSprite, "image_blend", Sprite_get_blend, 0 );
    rb_define_method( cSprite, "image_blend=", Sprite_set_blend, 1 );

    rb_define_method( cSprite, "add_animation", Sprite_add_animation, -1 );
    rb_define_method( cSprite, "start_animation", Sprite_start_animation, -1 );
    rb_define_method( cSprite, "change_animation", Sprite_change_animation, -1 );
    rb_define_method( cSprite, "pause_animation", Sprite_pause_animation, 0 );
    rb_define_method( cSprite, "resume_animation", Sprite_resume_animation, 0 );
    rb_define_method( cSprite, "animation_image", Sprite_get_anime_image, 0 );
    rb_define_method( cSprite, "animation_image=", Sprite_set_anime_image, 1 );

    rb_define_method( cSprite, "param_hash", Sprite_get_param_hash, 0 );
    rb_define_method( cSprite, "draw", Sprite_internal_draw, 0 );
    rb_define_method( cSprite, "update", Sprite_internal_update, 0 );
    rb_define_method( cSprite, "call_update_flag", Sprite_get_call_update_flag, 0 );
    rb_define_method( cSprite, "call_update_flag=", Sprite_set_call_update_flag, 1 );
    rb_define_method( cSprite, "call_draw_flag", Sprite_get_call_draw_flag, 0 );
    rb_define_method( cSprite, "call_draw_flag=", Sprite_set_call_draw_flag, 1 );

    rb_define_method( cSprite, "start_command", Sprite_start_command, 1 );

    rb_define_method( cSprite, "hit", Sprite_dummy, 1 );
    rb_define_method( cSprite, "shot", Sprite_dummy, 1 );

    rb_define_method( cSprite, "collision", Sprite_get_collision, 0 );
    rb_define_method( cSprite, "collision=", Sprite_set_collision, 1 );
    rb_define_method( cSprite, "collision_enable", Sprite_get_collision_enable_flag, 0 );
    rb_define_method( cSprite, "collision_enable=", Sprite_set_collision_enable_flag, 1 );

    rb_define_alloc_func( cSprite, Sprite_allocate );


    /* Matrixクラス定義 */
    cMatrix = rb_define_class_under( mDXRubyFw, "Matrix", rb_cObject );

    /* Matrixクラスにクラスメソッド登録*/
    rb_define_singleton_method( cMatrix, "create_rotation", Matrix_create_rot, 1 );
    rb_define_singleton_method( cMatrix, "create_scaling", Matrix_create_scale, 2 );
    rb_define_singleton_method( cMatrix, "create_translation", Matrix_create_trans, 2 );

    /* Matrixクラスにメソッド登録*/
    rb_define_private_method( cMatrix, "initialize", Matrix_initialize, -1 );
    rb_define_method( cMatrix, "*", Matrix_mul, 1 );
    rb_define_method( cMatrix, "to_s", Matrix_to_s, 0 );

    /* Matrixオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cMatrix, Matrix_allocate );


    /* Vectorクラス定義 */
    cVector = rb_define_class_under( mDXRubyFw, "Vector", rb_cObject );

    /* Vectorクラスにクラスメソッド登録*/
    rb_define_singleton_method( cVector, "distance", Vector_distance, 2 );
    rb_define_singleton_method( cVector, "cross_product", Vector_cross_product, 2 );
    rb_define_singleton_method( cVector, "inner_product", Vector_inner_product, 2 );

    /* Vectorクラスにメソッド登録*/
    rb_define_private_method( cVector, "initialize", Vector_initialize, -1 );
    rb_define_method( cVector, "*", Vector_mul, 1 );
    rb_define_method( cVector, "+", Vector_add, 1 );
    rb_define_method( cVector, "-", Vector_sub, 1 );
    rb_define_method( cVector, "-@", Vector_minus, 0 );
    rb_define_method( cVector, "/", Vector_div, 1 );
    rb_define_method( cVector, "to_s", Vector_to_s, 0 );
    rb_define_method( cVector, "x", Vector_get_x, 0 );
    rb_define_method( cVector, "y", Vector_get_y, 0 );
    rb_define_method( cVector, "z", Vector_get_z, 0 );
    rb_define_method( cVector, "w", Vector_get_w, 0 );
    rb_define_method( cVector, "size", Vector_get_size, 0 );
    rb_define_method( cVector, "normalize", Vector_normalize, 0 );

    /* Vectorオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cVector, Vector_allocate );

    /* 定数登録 */
    rb_define_const( mDXRubyFw, "DXRUBYFW_VERSION", rb_str_new2( DXRUBYFW_VERSION ) );

    {
        VALUE temp;
        temp = rb_eval_string( "$dxrubyfw_no_include" );
        if( temp == Qfalse || temp == Qnil )
        {
            rb_include_module( rb_cObject, mDXRubyFw );
        }
    }

    id_out = rb_intern( "out" );
    id_draw = rb_intern( "draw" );
    id_hit = rb_intern( "hit" );
    id_shot = rb_intern( "shot" );
    id_update = rb_intern( "update" );
    id_draw = rb_intern( "draw" );
    id_delete_flag = rb_intern( "deleted?" );

    id_set_x = rb_intern( "set_x" );
    id_set_y = rb_intern( "set_y" );
    id_set_dx = rb_intern( "set_dx" );
    id_set_dy = rb_intern( "set_dy" );
    id_set_speed = rb_intern( "set_speed" );
    id_set_angle = rb_intern( "set_angle" );
    id_rotate = rb_intern( "rotate" );
    id_rotate_image = rb_intern( "rotate_image" );
    id_accel = rb_intern( "accel" );
    id_add_dx = rb_intern( "add_dx" );
    id_add_dy = rb_intern( "add_dy" );
    id_add_alpha = rb_intern( "add_alpha" );
    id_wait = rb_intern( "wait" );
    id_call_command = rb_intern( "call_command" );
    id_delete = rb_intern( "delete" );

    /* 例外定義 */
    eDXRubyFwError = rb_define_class_under( cSprite, "DXRubyFwError", rb_eRuntimeError );

    /* 定数定義 */
    rb_define_const( cSprite, "BLEND_ALPHA", INT2FIX(0) );
    rb_define_const( cSprite, "BLEND_ADD"  , INT2FIX(4) );
    rb_define_const( cSprite, "BLEND_ADD1" , INT2FIX(4) );
    rb_define_const( cSprite, "BLEND_ADD2" , INT2FIX(5) );

    /* シンボル定義 */
    symbol_blend          = ID2SYM(rb_intern("blend"));
    symbol_angle          = ID2SYM(rb_intern("angle"));
    symbol_alpha          = ID2SYM(rb_intern("alpha"));
    symbol_scalex         = ID2SYM(rb_intern("scalex"));
    symbol_scaley         = ID2SYM(rb_intern("scaley"));
    symbol_centerx        = ID2SYM(rb_intern("centerx"));
    symbol_centery        = ID2SYM(rb_intern("centery"));
    symbol_z              = ID2SYM(rb_intern("z"));
    symbol_add            = ID2SYM(rb_intern("add"));
    symbol_add2           = ID2SYM(rb_intern("add2"));
    symbol_sub            = ID2SYM(rb_intern("sub"));
    symbol_sub2           = ID2SYM(rb_intern("sub2"));
}


