NewI\O nio_neko

nio_neko.nio is a familiar graphics animation demo. I ported it from the java version and added a sound effect.

Press ESC to exit.

Here is a screenshot:

Here is the sourcecode:

/******************************************************************************
 *
 *    nio_neko.c
 *
 *    Copyright (C) 2005  Chris Nystrom
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but without any warranty whatsoever.
 *
 *    For more license information see:
 *
 *      http://www.gnu.org/copyleft/gpl.html
 *
 *    Contact Author at:
 *
 *    Chris Nystrom
 *    11013 Prairie Dove Circle
 *    Austin, Texas  78758
 *
 *    E-Mail: cnystrom@gmail.com
 *    Blog:   http://conversazione.blogspot.com
 *    AIM:    nystromchris
 *
 *    Soli Deo Gloria
 *
 */

#include 
#include 
#include "nio_lib.h"

#define PI (3.14)

#define OVER  (1)
#define UNDER (2)
#define LEFT  (3)
#define RIGHT (4)

// Cat position and image numbers
// Most images have 2 versions for animation
#define UP1          (0)
#define UP2          (1)
#define UP_RIGHT1    (2)
#define UP_RIGHT2    (3)
#define RIGHT1       (4)
#define RIGHT2       (5)
#define DOWN_RIGHT1  (6)
#define DOWN_RIGHT2  (7)
#define DOWN1        (8)
#define DOWN2        (9)
#define DOWN_LEFT1  (10)
#define DOWN_LEFT2  (11)
#define LEFT1       (12)
#define LEFT2       (13)
#define UP_LEFT1    (14)
#define UP_LEFT2    (15)
#define UP_EDGE1    (16)
#define UP_EDGE2    (17)
#define RIGHT_EDGE1 (18)
#define RIGHT_EDGE2 (19)
#define DOWN_EDGE1  (20)
#define DOWN_EDGE2  (21)
#define LEFT_EDGE1  (22)
#define LEFT_EDGE2  (23)
#define SIT         (24)
#define YAWN        (25)
#define SCRATCH1    (26)
#define SCRATCH2    (27)
#define SLEEP1      (28)
#define SLEEP2      (29)
#define LICK        (30)
#define WAKEUP      (31)
#define CURSOR      (32)

static int pos;                 // Mouse's position in respect to the window
static int x, y;                // Mouse pos.
static int x2, y2;              // New Mouse pos.
static int ox, oy;              // Neko image pos.
static int dx, dy;              // Image-mouse distance
static int no;                  // Image number.
static int ilc1;                // Image loop counter
static int ilc2;                // Second image loop counter
static bool move;               // Mouse move flag
static bool out;                // Mouse in window flag
static double theta;            // Image-mouse polar data
static double dist;             // Distance

static int height;              // Window height
static int width;               // Window width

static int font;
static color black;
static color white;
static int images[33];
static int sound0;

int main(int argc, char *argv[])
{
        nio_point p;

        nio_app_init("nio_neko", "0.1");

        black = nio_get_color("black");
        white = nio_get_color("white");

        font = nio_load_sys_font(NIO_DEFAULT_FONT,
                                 NIO_DEFAULT_PTSIZE,
                                 NIO_STYLE_NORMAL, black, white);

        nio_set_screen(640, 480, white);

        nio_term_init(font, white, 10, 10, 34, 80);

        nio_print_at(1, 1, "Downloading resources...", TRUE);

        nio_cursor(OFF);

        move = FALSE;
        out = FALSE;
        ilc1 = 0;
        ilc2 = 0;
        pos = 0;

        x = 320 - 8;
        y = 240 - 16;

        images[UP1] = nio_load_image("nio_neko/1.GIF");
        images[UP2] = nio_load_image("nio_neko/2.GIF");
        images[UP_RIGHT1] = nio_load_image("nio_neko/3.GIF");
        images[UP_RIGHT2] = nio_load_image("nio_neko/4.GIF");
        images[RIGHT1] = nio_load_image("nio_neko/5.GIF");
        images[RIGHT2] = nio_load_image("nio_neko/6.GIF");
        images[DOWN_RIGHT1] = nio_load_image("nio_neko/7.GIF");
        images[DOWN_RIGHT2] = nio_load_image("nio_neko/8.GIF");
        images[DOWN1] = nio_load_image("nio_neko/9.GIF");
        images[DOWN2] = nio_load_image("nio_neko/10.GIF");
        images[DOWN_LEFT1] = nio_load_image("nio_neko/11.GIF");
        images[DOWN_LEFT2] = nio_load_image("nio_neko/12.GIF");
        images[LEFT1] = nio_load_image("nio_neko/13.GIF");
        images[LEFT2] = nio_load_image("nio_neko/14.GIF");
        images[UP_LEFT1] = nio_load_image("nio_neko/15.GIF");
        images[UP_LEFT2] = nio_load_image("nio_neko/16.GIF");
        images[UP_EDGE1] = nio_load_image("nio_neko/17.GIF");
        images[UP_EDGE2] = nio_load_image("nio_neko/18.GIF");
        images[RIGHT_EDGE1] = nio_load_image("nio_neko/19.GIF");
        images[RIGHT_EDGE2] = nio_load_image("nio_neko/20.GIF");
        images[DOWN_EDGE1] = nio_load_image("nio_neko/21.GIF");
        images[DOWN_EDGE2] = nio_load_image("nio_neko/22.GIF");
        images[LEFT_EDGE1] = nio_load_image("nio_neko/23.GIF");
        images[LEFT_EDGE2] = nio_load_image("nio_neko/24.GIF");
        images[SIT] = nio_load_image("nio_neko/25.GIF");
        images[YAWN] = nio_load_image("nio_neko/26.GIF");
        images[SCRATCH1] = nio_load_image("nio_neko/27.GIF");
        images[SCRATCH2] = nio_load_image("nio_neko/28.GIF");
        images[SLEEP1] = nio_load_image("nio_neko/29.GIF");
        images[SLEEP2] = nio_load_image("nio_neko/30.GIF");
        images[LICK] = nio_load_image("nio_neko/31.GIF");
        images[WAKEUP] = nio_load_image("nio_neko/32.GIF");
        images[CURSOR] = nio_load_image("nio_neko/cursor.jpg");

        sound0 = nio_load_sound("nio_neko/catmeow.wav");

        nio_clear_screen(white, TRUE);

        while (1) {

                int key;
                mouse_state ms;

                p = nio_get_screen_size();

                width = p.x;
                height = p.y;

                key = nio_get_key();
                ms = nio_mouse_state();

                if (ms.b1) {
                        nio_play_sound(sound0, 0);
                }

                if (ms.b3 || key == ESC) {
                        break;
                }

                x2 = ms.x;
                y2 = ms.y;


                move = FALSE;
                out = FALSE;

                // best I can do since SDL does not provide mouse events out
                // of window
                if (x2 == 0 || x2 == (width - 1) || y2 == 0
                    || y2 == (height - 1)) {
                        out = TRUE;
                }


                if (y2 < 32) {
                        y2 = 32;
                        pos = OVER;
                }

                if (y2 >= height - 2)
                        pos = UNDER;

                if (x2 < 16) {
                        x2 = 16;
                        pos = LEFT;
                }

                if (x2 >= width - 16) {
                        //              x2 = width - 16;
                        pos = RIGHT;
                }

                if ((x != x2) || (y != y2)) {
                        move = TRUE;
                        x = x2;
                        y = y2;
                }

                dx = x - ox;
                dy = oy - y;

                // Distance formula (from mouse to cat)
                dist = sqrt(dx * dx + dy * dy);

                // Angle from mouse to cat
                theta = atan2(dy, dx);


                // If moves cat 16 pixels...
                if (dist > 16) {

                        ox = (int) (ox + cos(theta) * 16);
                        oy = (int) (oy - sin(theta) * 16);
                        dist = dist - 16;

//   The following conditions determine what image should be shown.
//   There are two images for each action. For example if the cat's
//   going right, display the cat with open legs and then with close legs,
//   open, and so on.

                        // If right...
                        if (theta >= -PI / 8 && theta <= PI / 8)
                                no = (no == RIGHT1) ? RIGHT2 : RIGHT1;

                        // If upper-right...
                        if (theta > PI / 8 && theta < 3 * PI / 8)
                                no = (no ==
                                      UP_RIGHT1) ? UP_RIGHT2 : UP_RIGHT1;

                        // If up...
                        if (theta >= 3 * PI / 8 && theta <= 5 * PI / 8)
                                no = (no == UP1) ? UP2 : UP1;

                        // If upper-left...
                        if (theta > 5 * PI / 8 && theta < 7 * PI / 8)
                                no = (no ==
                                      UP_LEFT1) ? UP_LEFT2 : UP_LEFT1;

                        // If left...
                        if (theta >= 7 * PI / 8 || theta <= -7 * PI / 8)
                                no = (no == LEFT1) ? LEFT2 : LEFT1;

                        // If bottom-left...
                        if (theta > -7 * PI / 8 && theta < -5 * PI / 8)
                                no = (no ==
                                      DOWN_LEFT1) ? DOWN_LEFT2 :
                                    DOWN_LEFT1;

                        // If down...
                        if (theta >= -5 * PI / 8 && theta <= -3 * PI / 8)
                                no = (no == DOWN1) ? DOWN2 : DOWN1;

                        // If bottom-right...
                        if (theta > -3 * PI / 8 && theta < -PI / 8)
                                no = (no ==
                                      DOWN_RIGHT1) ? DOWN_RIGHT2 :
                                    DOWN_RIGHT1;

                        move = FALSE;

                } else {

                        // Mouse hasn't moved or the cat is over the mouse

                        switch (no) {
                        case SIT:       // 
                                // If the mouse is out side the applet
                                if (out == TRUE) {
                                        switch (pos) {
                                        case OVER:
                                                no = UP_EDGE1;
                                                break;
                                        case UNDER:
                                                no = DOWN_EDGE1;
                                                break;
                                        case LEFT:
                                                no = LEFT_EDGE1;
                                                break;
                                        case RIGHT:
                                                no = RIGHT_EDGE1;
                                                break;
                                        default:
                                                no = LICK;
                                                break;
                                        }
                                        pos = 0;
                                        break;
                                }

                                no = LICK;
                                break;

                                // The mouse is outside, above applet
                        case UP_EDGE1:
                                // Show UP_EDGE images 6 times
                                no = UP_EDGE2;
                                ilc1++;
                                if (ilc1 == 6) {
                                        no = SCRATCH1;
                                        ilc1 = 0;
                                }
                                break;

                        case UP_EDGE2:
                                no = UP_EDGE1;
                                break;

                                // The mouse is outside, under applet
                        case DOWN_EDGE1:
                                // Show DOWN_EDGE images 6 times
                                no = DOWN_EDGE2;
                                ilc1++;
                                if (ilc1 == 6) {
                                        no = SCRATCH1;
                                        ilc1 = 0;
                                }
                                break;

                        case DOWN_EDGE2:
                                no = DOWN_EDGE1;
                                break;

                                // The mouse is outside, left
                        case LEFT_EDGE1:
                                // Show LEFT_EDGE images 6 times
                                no = LEFT_EDGE2;
                                ilc1++;
                                if (ilc1 == 6) {
                                        no = SCRATCH1;
                                        ilc1 = 0;
                                }
                                break;

                        case LEFT_EDGE2:
                                no = LEFT_EDGE1;
                                break;

                                // The mouse is outside, right
                        case RIGHT_EDGE1:
                                // Show RIGHT_EDGE images 6 times
                                no = RIGHT_EDGE2;
                                ilc1++;
                                if (ilc1 == 6) {
                                        no = SCRATCH1;
                                        ilc1 = 0;
                                }
                                break;

                        case RIGHT_EDGE2:
                                no = RIGHT_EDGE1;
                                break;

                        case LICK:      // Cat lick (6  times)
                                no = SIT;
                                ilc1++;
                                if (ilc1 == 6) {
                                        no = SCRATCH1;
                                        ilc1 = 0;
                                }
                                break;

                        case SCRATCH1:
                                no = SCRATCH2;
                                break;  // Cat scratch images 4 times

                        case SCRATCH2:
                                no = SCRATCH1;
                                ilc2++;
                                if (ilc2 == 4) {
                                        no = YAWN;
                                        ilc2 = 0;
                                }
                                break;

                        case YAWN:
                                no = SLEEP1;
                                break;

                                // Cat sleeps forever unless mouse moves
                        case SLEEP1:
                                no = SLEEP2;
                                break;

                        case SLEEP2:
                                no = SLEEP1;
                                break;

                        default:
                                no = SIT;
                                break;
                        }

                        // Mouse moved. Wakup cat.
                        if (move == TRUE) {
                                nio_play_sound(sound0, 0);
                                no = WAKEUP;
                                ilc1 = 0;
                                ilc2 = 0;
                                move = FALSE;
                        }
                }

                nio_clear_screen(white, FALSE);
                nio_draw_image(images[no], ox - 16, oy - 32, FALSE);
                nio_draw_image(images[CURSOR], ms.x, ms.y, FALSE);
                nio_paint();

                usleep(100000);
        }

        nio_cursor(ON);
        nio_exit();
        exit(0);
}

You can download the source code and resources here:

ftp://ftp.newio.org/pub/nio_apps/nio_neko.0.009_003_020106.tar.gz


Updated: 02/01/06 ccn