/* gnome-stones - objects/gnomekoban.c * * Time-stamp: <2003-07-27 05:17:59 callum> * * Copyright (C) 1998, 2003 Carsten Schaar * * 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; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "cave.h" #include "player.h" #include "status.h" #include "object.h" /*****************************************************************************/ /* Stone definitions */ static GStonesObject *OBJECT_FRAME; static GStonesObject *OBJECT_EMPTY; static GStonesObject *OBJECT_WALL; static GStonesObject *OBJECT_DIRT; static GStonesObject *OBJECT_CRATE; static GStonesObject *OBJECT_GNOME; static GStonesObject *OBJECT_ENTRANCE; static GStonesSignal SIGNAL_PLAYER_EXTRALIFE; static GStonesSignal SIGNAL_DOOR_OPEN; static GStonesSignal SIGNAL_CAVE_PRE_SCAN; static GStonesSignal SIGNAL_CAVE_POST_SCAN; static GStonesSignal SIGNAL_PLAYER_DIE; static GStonesSignal SIGNAL_PLAYER_START; gchar * objects_init (GStonesPlugin *plugin); /*****************************************************************************/ static void free_private_data (GStonesCave *cave, GStonesObjContext *context) { g_free (object_context_private_data (context)); } /*****************************************************************************/ /* EMPTY stuff */ typedef struct _EmptyData EmptyData; struct _EmptyData { /* Dynamic data. */ guint open_door_animation; guint extra_life_animation; }; static gboolean empty_init_cave (GStonesCave *cave, GStonesObjContext *context) { EmptyData *empty; empty= g_new0 (EmptyData, 1); if (!empty) return FALSE; object_context_set_private_data (context, empty); return TRUE; } static guint empty_animate (GStonesCave *cave, guint x, guint y, GStonesObjContext *context) { EmptyData *empty= (EmptyData *) object_context_private_data (context); if (cave->entry[x][y].state > 0) { guint f= cave->frame % 16; if (f > 8) return 16-f; else return f; } if (empty->open_door_animation) return empty->open_door_animation; else if (empty->extra_life_animation) return 9+g_random_int_range (0, INT_MAX) % 4; else return 0; } static void empty_signals (GStonesCave *cave, GStonesSignal signal, GStonesObjContext *context) { EmptyData *empty= (EmptyData *) object_context_private_data (context); if (signal == SIGNAL_CAVE_PRE_SCAN) { if (empty->open_door_animation) empty->open_door_animation--; if (empty->extra_life_animation) empty->extra_life_animation--; } else if (signal == SIGNAL_DOOR_OPEN) { empty->open_door_animation= 3; } else if (signal == SIGNAL_PLAYER_EXTRALIFE) { empty->extra_life_animation= 10; } } static GStonesObjectDesc empty_object= { "empty", empty_init_cave, free_private_data, NULL, empty_signals, "background.png", empty_animate, NULL, 0, 0 }; /*****************************************************************************/ /* CRATE stuff */ static void crate_signals (GStonesCave *cave, GStonesSignal signal, GStonesObjContext *context) { guint x; guint y; if (signal == SIGNAL_CAVE_POST_SCAN) { gboolean finished= TRUE; for (y= 1 ; y <= CAVE_MAX_HEIGHT; y++) for (x= 1 ; x <= CAVE_MAX_WIDTH; x++) if ((cave->entry[x][y].object == OBJECT_CRATE) && (cave->entry[x][y].state == 0)) finished= FALSE; if (finished) cave->flags|= CAVE_FINISHED; } } static GStonesObjectDesc crate_object= { "crate", NULL, NULL, NULL, crate_signals, "crate.png", NULL, NULL, 0, 0 }; /*****************************************************************************/ /* ENTRANCE stuff */ static void entrance_scanned (GStonesCave *cave, guint x, guint y, GStonesObjContext *context) { if (cave->entry[x][y].state > 0) { if (cave->entry[x][y].state == 3) { cave->entry[x][y].object= OBJECT_GNOME; cave->entry[x][y].state = 0; } else cave->entry[x][y].state++; } } static void entrance_signals (GStonesCave *cave, GStonesSignal signal, GStonesObjContext *context) { guint x; guint y; if (signal == SIGNAL_PLAYER_START) { for (y= 1 ; y <= CAVE_MAX_HEIGHT; y++) for (x= 1 ; x <= CAVE_MAX_WIDTH; x++) if (cave->entry[x][y].object == OBJECT_ENTRANCE) cave->entry[x][y].state= 1; } } static guint entrance_animate (GStonesCave *cave, guint x, guint y, GStonesObjContext *context) { if (cave->entry[x][y].state == 0) return (cave->frame % 4)/2; else return cave->entry[x][y].state+1; } static GStonesObjectDesc entrance_object= { "entrance", NULL, NULL, entrance_scanned, entrance_signals, "door.png", entrance_animate, NULL, 0, 0 }; /*****************************************************************************/ /* GNOME stuff */ static gboolean gnome_init_cave (GStonesCave *cave, GStonesObjContext *context) { guint x; guint y; for (y= 1 ; y <= CAVE_MAX_HEIGHT; y++) for (x= 1 ; x <= CAVE_MAX_WIDTH; x++) if (cave->entry[x][y].object == OBJECT_ENTRANCE) { cave->player_x= x; cave->player_y= y; } return TRUE; } /* The internal gnome states: 0: standing still an waiting. 1: pushing right 2: pushing left 3: walk right 4: walk left */ static void gnome_scanned (GStonesCave *cave, guint x, guint y, GStonesObjContext *context) { gint state= cave->entry[x][y].state << 4; if ((cave->player_x_direction != 0) || (cave->player_y_direction != 0)) { gboolean moved= FALSE; guint xn= x+cave->player_x_direction; guint yn= y+cave->player_y_direction; GStonesObject *type= cave->entry[xn][yn].object; if (type == OBJECT_EMPTY) { if (cave->player_x_direction > 0) state= 3; else if (cave->player_x_direction < 0) state= 4; moved= TRUE; } else { if (cave->player_x_direction > 0) state= 1; else if (cave->player_x_direction < 0) state= 2; if ((type == OBJECT_CRATE) && (cave->entry[xn+cave->player_x_direction][yn+cave->player_y_direction].object == OBJECT_EMPTY)) { moved= TRUE; cave->entry[xn+cave->player_x_direction][yn+cave->player_y_direction].object= type; } } if (moved) { if (cave->player_push) { cave->entry[xn][yn].object= OBJECT_EMPTY; } else { cave->entry[xn][yn].object = OBJECT_GNOME; cave->entry[xn][yn].scanned= TRUE; cave->entry[x][y].object = OBJECT_EMPTY; cave->entry[x][y].state = cave->entry[x][y].state & 0xf; cave->player_x= xn; cave->player_y= yn; x= xn; y= yn; } } } cave->entry[x][y].state= (cave->entry[x][y].state & 0xf)+(state << 4); } static void gnome_signals (GStonesCave *cave, GStonesSignal signal, GStonesObjContext *context) { if (signal == SIGNAL_PLAYER_DIE) { if (cave->flags & CAVE_PLAYER_EXISTS) { /* explosion_new (cave, cave->player_x, cave->player_y, TRUE); */ cave->flags&= ~CAVE_PLAYER_EXISTS; cave->flags|= CAVE_FINISHED; } } } typedef struct _GnomeAnimState GnomeAnimState; typedef struct _GnomeAnimSequence GnomeAnimSequence; struct _GnomeAnimState { guint sequence:8; guint offset :8; }; struct _GnomeAnimSequence { guint probability; guint start; guint length; /* The probability, that this sequence will be played again directly after finishing. The value must in a range of [0...100]. */ guint repeat_prob; }; GnomeAnimSequence gnome_anim[]= { { 15, 0, 4, 20 }, { 10, 4, 4, 20 } }; static guint gnome_animate (GStonesCave *cave, guint x, guint y, GStonesObjContext *context) { register guint idx = 0; register gint state= cave->entry[x][y].state >> 4; GnomeAnimState anim; anim.sequence = cave->entry[x][y].anim_state >> 8; anim.offset = cave->entry[x][y].anim_state & 0xff; switch (state) { case 0: { if (anim.sequence == 0) { int rnd = g_random_int_range (0, INT_MAX) % 100; for (anim.sequence = sizeof (gnome_anim)/sizeof (gnome_anim[1]); anim.sequence > 0; anim.sequence--) { if (gnome_anim[anim.sequence-1].probability > rnd) break; else rnd-= gnome_anim[anim.sequence-1].probability; } anim.offset= 0; } if (anim.sequence) { idx= gnome_anim [anim.sequence-1].start+anim.offset; anim.offset++; if (anim.offset >= gnome_anim [anim.sequence-1].length) { anim.offset= 0; if ((g_random_int_range (0, INT_MAX) % 100) >= gnome_anim [anim.sequence-1].repeat_prob) anim.sequence= 0; } } else idx= 0; } break; case 1: case 2: anim.sequence= 0; idx= 8*state+cave->frame % 8; break; case 3: case 4: anim.sequence= 0; idx= 12+4*state+cave->frame % 4; break; default: anim.sequence= 0; idx= 0; break; } cave->entry[x][y].anim_state = (anim.sequence << 8) | anim.offset; return idx; } static GStonesObjectDesc gnome_object= { "gnome", gnome_init_cave, NULL, gnome_scanned, gnome_signals, "gnome.png", gnome_animate, NULL, 0, 0 }; /*****************************************************************************/ /* Additional stuff. */ static GStonesObjectDesc dirt_object= { "dirt", NULL, NULL, NULL, NULL, "dirt.png", NULL, NULL, 0, 0 }; static GStonesObjectDesc wall_object= { "wall", NULL, NULL, NULL, NULL, "wall.png", NULL, NULL, 0, 0 }; /*****************************************************************************/ /* Register all objects. */ gchar * objects_init (GStonesPlugin *plugin) { OBJECT_FRAME = object_find_object_by_name ("default:frame"); OBJECT_EMPTY = object_register (plugin, &empty_object); OBJECT_WALL = object_register (plugin, &wall_object); OBJECT_DIRT = object_register (plugin, &dirt_object); OBJECT_CRATE = object_register (plugin, &crate_object); OBJECT_GNOME = object_register (plugin, &gnome_object); OBJECT_ENTRANCE = object_register (plugin, &entrance_object); SIGNAL_PLAYER_EXTRALIFE= gstones_signal ("player.extralife"); SIGNAL_CAVE_PRE_SCAN = gstones_signal ("cave.pre_scan"); SIGNAL_CAVE_POST_SCAN = gstones_signal ("cave.post_scan"); SIGNAL_PLAYER_DIE = gstones_signal ("player.die"); SIGNAL_PLAYER_START = gstones_signal ("player.start"); return "gnomekoban"; } /* Local Variables: */ /* mode:c */ /* eval:(load-library "time-stamp") */ /* eval:(make-local-variable 'write-file-hooks) */ /* eval:(add-hook 'write-file-hooks 'time-stamp) */ /* eval:(setq time-stamp-format '(time-stamp-yyyy/mm/dd time-stamp-hh:mm:ss user-login-name)) */ /* End: */