/* * Copyright (C) 2003 Benjamin Otte * * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "game-private.h" #include "game-sprite.h" #include "game-marshal.h" #include static void game_sprite_class_init (gpointer g_class, gpointer class_data); static void game_sprite_init (GTypeInstance * instance, gpointer g_class); static void game_sprite_get_property (GObject * object, guint param_id, GValue * value, GParamSpec * pspec); static void game_sprite_set_property (GObject * object, guint param_id, const GValue * value, GParamSpec * pspec); enum { MOVE_TO, /* fill me */ LAST_SIGNAL }; enum { PROP_0, PROP_GRAPHIC, PROP_BOARD, PROP_LAYER, PROP_X, PROP_Y, PROP_POSITION }; static GObjectClass *parent_class = NULL; static guint game_sprite_signals[LAST_SIGNAL] = { 0 }; /* the area is relative to the board, so the movement by sprite->pos has been applied already */ static void game_sprite_invalidate_area (GameSprite *sprite, const GameRectangle *areap) { GameBoard *board = sprite->board; GameRectangle inval, rect, area = *areap; int x, y, xmax, ymax; if (board->repeat.x > 0.0) { xmax = 3; rect.x1 = 0; rect.x2 = board->repeat.x; area.x1 -= board->repeat.x; area.x2 -= board->repeat.x; } else { xmax = 1; rect.x1 = area.x1; rect.x2 = area.x2; } if (board->repeat.y > 0.0) { ymax = 3; rect.y1 = 0; rect.y2 = board->repeat.y; area.y1 -= board->repeat.y; area.y2 -= board->repeat.y; } else { ymax = 1; rect.y1 = area.y1; rect.y2 = area.y2; } for (x = 0; x < xmax; x++) { for (y = 0; y < ymax; y++) { if (game_rectangle_intersect (&inval, &rect, &area)) game_graphic_invalidate_area (GAME_GRAPHIC (board), &inval); area.y1 += board->repeat.y; area.y2 += board->repeat.y; } area.y1 -= ymax * board->repeat.y; area.y2 -= ymax * board->repeat.y; area.x1 += board->repeat.x; area.x2 += board->repeat.x; } } GType game_sprite_get_type () { static GType sprite_type = 0; if (!sprite_type) { static const GTypeInfo sprite_info = { sizeof (GameSpriteClass), NULL, NULL, game_sprite_class_init, NULL, NULL, sizeof (GameSprite), 0, game_sprite_init, }; sprite_type = g_type_register_static (GAME_TYPE_OBJECT, "GameSprite", &sprite_info, 0); } return sprite_type; } static gboolean game_sprite_do_move_to (GameSprite *sprite, const GamePoint *dest) { g_object_freeze_notify (G_OBJECT (sprite)); sprite->pos = *dest; if (sprite->board->repeat.x > 0.0) { sprite->pos.x = fmod (sprite->pos.x, sprite->board->repeat.x); if (sprite->pos.x < 0.0) sprite->pos.x += sprite->board->repeat.x; } if (sprite->board->repeat.y > 0.0) { sprite->pos.y = fmod (sprite->pos.y, sprite->board->repeat.y); if (sprite->pos.y < 0.0) sprite->pos.y += sprite->board->repeat.y; } g_object_notify (G_OBJECT (sprite), "x"); g_object_notify (G_OBJECT (sprite), "y"); g_object_notify (G_OBJECT (sprite), "position"); g_object_thaw_notify (G_OBJECT (sprite)); return TRUE; } static gchar * game_sprite_to_string (GameObject *object) { GameSprite *sprite = GAME_SPRITE (object); return g_strdup_printf ("%s %p @ (%g,%g)", G_OBJECT_TYPE_NAME(object), object, sprite->pos.x, sprite->pos.y); } static void game_sprite_dispose (GObject *object) { GameRectangle rect; GameSprite *sprite = GAME_SPRITE (object); game_sprite_set_graphic (sprite, NULL); if (sprite->board) { game_container_remove (GAME_CONTAINER (sprite->board), GAME_OBJECT (sprite)); game_sprite_get_area (sprite, &rect); game_sprite_invalidate_area (sprite, &rect); sprite->board->sprites = g_slist_remove (sprite->board->sprites, sprite); sprite->board = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } static void game_sprite_class_init (gpointer g_class, gpointer class_data) { GameSpriteClass *sprite_class = GAME_SPRITE_CLASS (g_class); GameObjectClass *gameobject_class = GAME_OBJECT_CLASS (g_class); GObjectClass *object_class = G_OBJECT_CLASS (g_class); parent_class = g_type_class_peek_parent (g_class); sprite_class->move_to = game_sprite_do_move_to; gameobject_class->to_string = game_sprite_to_string; object_class->dispose = game_sprite_dispose; object_class->get_property = game_sprite_get_property; object_class->set_property = game_sprite_set_property; game_sprite_signals[MOVE_TO] = g_signal_new ("move-to", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GameSpriteClass, move_to), _game_accu_abort, NULL, game_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GAME_TYPE_POINT | G_SIGNAL_TYPE_STATIC_SCOPE); g_object_class_install_property (object_class, PROP_GRAPHIC, g_param_spec_object ("graphic", _("graphic"), _("graphic to be displayed"), GAME_TYPE_GRAPHIC, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_BOARD, g_param_spec_object ("board", _("board"), _("board this sprite is used on"), GAME_TYPE_GRAPHIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_LAYER, g_param_spec_int ("layer", _("layer"), _("layer this sprite is put in (aka z-order)"), G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_X, g_param_spec_double ("x", _("x"), _("x position of sprite"), -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_Y, g_param_spec_double ("y", _("y"), _("y position of sprite"), -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_POSITION, g_param_spec_boxed ("position", _("position"), _("position of sprite"), GAME_TYPE_POINT, G_PARAM_READWRITE)); } static void game_sprite_init (GTypeInstance *instance, gpointer g_class) { } static void game_sprite_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GameSprite *sprite = GAME_SPRITE (object); switch (param_id) { case PROP_GRAPHIC: g_value_set_object (value, game_sprite_get_graphic (sprite)); break; case PROP_BOARD: g_value_set_object (value, sprite->board); break; case PROP_LAYER: g_value_set_int (value, game_sprite_get_layer (sprite)); break; case PROP_X: g_value_set_double (value, sprite->pos.x); break; case PROP_Y: g_value_set_double (value, sprite->pos.y); break; case PROP_POSITION: g_value_set_boxed (value, &sprite->pos); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void game_sprite_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GameSprite *sprite = GAME_SPRITE (object); switch (param_id) { case PROP_GRAPHIC: game_sprite_set_graphic (sprite, GAME_GRAPHIC (g_value_get_object (value))); break; case PROP_BOARD: g_assert (sprite->board == NULL); sprite->board = GAME_BOARD (g_value_get_object (value)); g_assert (sprite->board != NULL); break; case PROP_LAYER: game_sprite_set_layer (sprite, g_value_get_int (value)); break; case PROP_X: game_sprite_set_position (sprite, g_value_get_double (value), sprite->pos.y); break; case PROP_Y: game_sprite_set_position (sprite, sprite->pos.x, g_value_get_double (value)); break; case PROP_POSITION: { GamePoint *p = g_value_get_boxed (value); game_sprite_set_position (sprite, p->x, p->y); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } GameSprite * game_sprite_new (GameBoard *board, double x, double y) { GameSprite *ret; g_return_val_if_fail (GAME_IS_BOARD (board), NULL); ret = game_board_add_sprite (board, GAME_TYPE_SPRITE, "x", x, "y", y, NULL); return ret; } static void game_sprite_graphic_invalidate (GameGraphic *graphic, const GameRectangle *area, GameSprite *sprite) { GameRectangle inval; g_assert (graphic == sprite->graphic); game_rectangle_move (&inval, area, &sprite->pos); game_sprite_invalidate_area (sprite, &inval); } void game_sprite_set_graphic (GameSprite *sprite, GameGraphic *graphic) { GameRectangle prev, now; g_return_if_fail (GAME_IS_SPRITE (sprite)); g_return_if_fail (graphic == NULL || GAME_IS_GRAPHIC (graphic)); if (sprite->graphic == graphic) return; game_sprite_get_area (sprite, &prev); if (graphic) { g_object_ref (graphic); g_signal_connect (graphic, "invalidate", G_CALLBACK (game_sprite_graphic_invalidate), sprite); } if (sprite->graphic) { if (g_signal_handlers_disconnect_by_func (sprite->graphic, game_sprite_graphic_invalidate, sprite) != 1) { g_assert_not_reached (); } g_object_unref (sprite->graphic); } sprite->graphic = graphic; game_sprite_get_area (sprite, &now); if (sprite->board) { game_rectangle_union (&now, &prev, &now); game_sprite_invalidate_area (sprite, &now); } g_object_notify (G_OBJECT (sprite), "graphic"); } GameGraphic * game_sprite_get_graphic (GameSprite *sprite) { g_return_val_if_fail (GAME_IS_SPRITE (sprite), NULL); return sprite->graphic; } void _game_board_sprite_changed_layer (GameBoard *board, GameSprite *sprite); void game_sprite_set_layer (GameSprite *sprite, int layer) { g_return_if_fail (GAME_IS_SPRITE (sprite)); if (sprite->layer == layer) return; sprite->layer = layer; if (sprite->board) _game_board_sprite_changed_layer (sprite->board, sprite); g_object_notify (G_OBJECT (sprite), "layer"); } int game_sprite_get_layer (GameSprite *sprite) { g_return_val_if_fail (GAME_IS_SPRITE (sprite), 0); return sprite->layer; } gboolean game_sprite_move (GameSprite *sprite, double diff_x, double diff_y) { g_return_val_if_fail (GAME_IS_SPRITE (sprite), FALSE); return game_sprite_set_position (sprite, sprite->pos.x + diff_x, sprite->pos.y + diff_y); } gboolean game_sprite_set_position (GameSprite *sprite, double x, double y) { gboolean succeeded; GameRectangle prev, now; GamePoint dest = { x, y }; g_return_val_if_fail (GAME_IS_SPRITE (sprite), FALSE); game_sprite_get_area (sprite, &prev); g_signal_emit (sprite, game_sprite_signals[MOVE_TO], 0, &dest, &succeeded); if (succeeded) { game_sprite_get_area (sprite, &now); game_rectangle_union (&now, &prev, &now); game_sprite_invalidate_area (sprite, &now); } return succeeded; } void game_sprite_get_area (GameSprite *sprite, GameRectangle *area) { g_return_if_fail (GAME_IS_SPRITE (sprite)); g_return_if_fail (area != NULL); if (sprite->graphic == NULL) { GAME_RECTANGLE_INVALIDATE (area); return; } game_rectangle_move (area, &sprite->graphic->rect, &sprite->pos); }