Commit a28585ae authored by Jonathan Michalon's avatar Jonathan Michalon

Translated into vala last module: game.c & game.h

parent b11f4369
......@@ -5,7 +5,7 @@ libovcc_la_CPPFLAGS = -DG_LOG_DOMAIN=\"libovcc\"
libovcc_la_SOURCES = enumtypes.c \
ovcc-marshal.c \
board.vala \
game.c \
game.vala \
pawn.vala \
player.vala \
stack.vala \
......@@ -15,8 +15,7 @@ libovcc_la_SOURCES = enumtypes.c \
tilesdef.vala \
utils.vala \
xmlutils.c
ovccinclude_HEADERS = ovcc.h \
game.h
ovccinclude_HEADERS = ovcc.h
test_LDADD = libovcc.la -lpthread
test_SOURCES = test.c
......
/*
*
* Copyright (C) 2009 Colomban Wendling <ban@herbesfolles.org>
* Jonathan Michalon <studios.chalmion@no-log.org>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "game.h"
#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include "enumtypes.h"
#include "stack.h"
#include "board.h"
#include "tileset.h"
#include "player.h"
#include "tile.h"
/**
* SECTION: game
* @short_description: The OVCC game management module
* @include: libovcc/game.h
*
* The main part of OVCC. This is the glue connecting all the game's parts to
* make a playable game, and play it.
*/
/**
* OVCCGame:
*
* Opaque object representing a game.
*/
struct _OVCCGamePrivate
{
OVCCGameState state;
OVCCTileSet *tileset;
OVCCStack *stack;
OVCCBoard *board;
GSList *players;
gsize n_players;
OVCCPlayer *current_player;
};
enum
{
SIGNAL_PLAYER_ADDED,
SIGNAL_PLAYER_REMOVED,
SIGNAL_UNPLACEABLE_TILE,
N_SIGNALS
};
enum
{
PROP_0,
PROP_STATE,
PROP_CURRENT_TILE,
PROP_CURRENT_PLAYER,
PROP_TILE_SET
};
G_DEFINE_TYPE (OVCCGame, ovcc_game, G_TYPE_OBJECT)
static guint ovcc_game_signals[N_SIGNALS] = { 0 };
#define IS_CURRENT_PLAYER(game, player) \
((player) == (game)->priv->current_player)
#define GAME_IS_STARTED(game) \
((game)->priv->state == OVCC_GAME_STATE_STARTED)
GQuark
ovcc_game_error_quark (void)
{
static GQuark error_quark = 0;
if (G_UNLIKELY (error_quark == 0)) {
error_quark = g_quark_from_static_string ("OVCCGameError");
}
return error_quark;
}
static void
ovcc_game_finalize (GObject *object)
{
OVCCGame *game = OVCC_GAME (object);
ovcc_tileset_unref (game->priv->tileset);
g_object_unref (game->priv->stack);
g_object_unref (game->priv->board);
g_slist_foreach (game->priv->players, g_object_unref, NULL);
g_slist_free (game->priv->players);
G_OBJECT_CLASS (ovcc_game_parent_class)->finalize (object);
}
static void
ovcc_game_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
OVCCGame *game = OVCC_GAME (object);
switch (prop_id)
{
case PROP_TILE_SET:
game->priv->tileset = ovcc_tileset_ref (g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ovcc_game_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
OVCCGame *game = OVCC_GAME (object);
switch (prop_id)
{
case PROP_STATE:
g_value_set_enum (value, ovcc_game_get_state (game));
break;
case PROP_CURRENT_TILE:
g_value_set_object (value, ovcc_game_get_current_tile (game));
break;
case PROP_CURRENT_PLAYER:
g_value_set_object (value, ovcc_game_get_current_player (game));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ovcc_game_set_state (OVCCGame *game,
OVCCGameState state)
{
/* hack, should never happen */
g_return_if_fail (game->priv->state != state);
game->priv->state = state;
g_object_notify (G_OBJECT (game), "state");
}
/* notify property current-tile changes */
static void
on_item_removed (OVCCStack *stack,
OVCCTile *tile,
gpointer data)
{
OVCCGame *self = OVCC_GAME (data);
g_object_notify (G_OBJECT (self), "current-tile");
if (ovcc_stack_size (stack) <= 0) {
ovcc_game_set_state (self, OVCC_GAME_STATE_STOPPED);
}
}
static void
on_current_tile_changed (GObject *object,
GParamSpec *pspec,
gpointer data)
{
OVCCGame *self = OVCC_GAME (object);
if (GAME_IS_STARTED (self)) {
OVCCTile *tile;
tile = ovcc_game_get_current_tile (self);
if (tile) {
if (! ovcc_board_is_tile_placeable (self->priv->board, tile)) {
g_signal_emit (self, ovcc_game_signals[SIGNAL_UNPLACEABLE_TILE], 0, tile);
if (ovcc_stack_size (self->priv->stack) < 2) {
/* there is no tile left to mix with */
ovcc_game_abort (self);
} else {
g_object_unref (ovcc_stack_pop (self->priv->stack));
/* FIXME: make sure the tile is not pushed at top */
ovcc_stack_add (self->priv->stack, tile);
}
}
g_object_unref (tile);
}
}
}
static void
ovcc_game_init (OVCCGame *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
OVCC_TYPE_GAME,
OVCCGamePrivate);
self->priv->state = OVCC_GAME_STATE_STOPPED;
self->priv->tileset = NULL;
self->priv->stack = ovcc_stack_new ();
self->priv->board = ovcc_board_new (self->priv->stack);
self->priv->players = NULL;
self->priv->n_players = 0;
self->priv->current_player = NULL;
g_signal_connect (self->priv->stack, "item-removed", G_CALLBACK (on_item_removed), self);
g_signal_connect (self, "notify::current-tile", G_CALLBACK (on_current_tile_changed), NULL);
}
static void
ovcc_game_constructed (GObject *object)
{
OVCCGame *game = OVCC_GAME (object);
if (! game->priv->tileset) {
g_critical ("The OVCCGame::tileset construct property must be set");
}
}
static void
ovcc_game_class_init (OVCCGameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ovcc_game_finalize;
object_class->get_property = ovcc_game_get_property;
object_class->set_property = ovcc_game_set_property;
object_class->constructed = ovcc_game_constructed;
klass->player_added = NULL;
klass->player_removed = NULL;
g_object_class_install_property (object_class, PROP_STATE,
g_param_spec_enum ("state",
"State",
"Game state",
OVCC_TYPE_GAME_STATE,
OVCC_GAME_STATE_STOPPED,
G_PARAM_READABLE));
g_object_class_install_property (object_class, PROP_CURRENT_TILE,
g_param_spec_object ("current-tile",
"Current tile",
"Game's current tile",
OVCC_TYPE_TILE,
G_PARAM_READABLE));
g_object_class_install_property (object_class, PROP_CURRENT_PLAYER,
g_param_spec_object ("current-player",
"Current player",
"Game's current player",
OVCC_TYPE_PLAYER,
G_PARAM_READABLE));
g_object_class_install_property (object_class, PROP_TILE_SET,
g_param_spec_boxed ("tileset",
"TileSet",
"TileSet",
OVCC_TYPE_TILESET,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
/**
* OVCCGame::player-added:
* @game: The object that received the signal
* @player: The player that was just added
*
* This signal gets emitted when a player joined the game.
*/
ovcc_game_signals[SIGNAL_PLAYER_ADDED] = g_signal_new ("player-added",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (OVCCGameClass, player_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
OVCC_TYPE_PLAYER);
/**
* OVCCGame::player-removed:
* @game: The object that received the signal
* @player: The player that was just removed
*
* This signal gets emitted when a player leaves the game.
*/
ovcc_game_signals[SIGNAL_PLAYER_REMOVED] = g_signal_new ("player-removed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (OVCCGameClass, player_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
OVCC_TYPE_PLAYER);
/**
* OVCCGame::unplaceable-tile:
* @game: The object that received the signal
* @tile: The tile that cannot be placed
*
* This signal gets emitted when an unplaceable tile become the current one.
* This is meant to be able to show a notification to the user, but may be
* safely ignored as the game tries to recover the situation by itself if
* possible (it re-puts the tile on the stack, hoping for a future placement)
* or aborts itself.
*/
ovcc_game_signals[SIGNAL_UNPLACEABLE_TILE] = g_signal_new ("unplaceable-tile",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (OVCCGameClass, unplaceable_tile),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
OVCC_TYPE_TILE);
g_type_class_add_private (klass, sizeof (OVCCGamePrivate));
}
/**
* ovcc_game_new:
*
* Creates a new Game.
*
* Returns: The newly created #OVCCGame.
*/
OVCCGame *
ovcc_game_new (OVCCTileSet *set)
{
return g_object_new (OVCC_TYPE_GAME, "tileset", set, NULL);
}
/**
* ovcc_game_start:
* @game: An #OVCCGame
*
* Starts a game. This needs at least two players.
*
* Returns: Whether game is started (and not whether game just started)
*/
gboolean
ovcc_game_start (OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), FALSE);
if (game->priv->n_players >= 2 && ! GAME_IS_STARTED (game)) {
game->priv->current_player = game->priv->players->data;
ovcc_stack_clear (game->priv->stack);
ovcc_stack_fill (game->priv->stack, game->priv->tileset);
ovcc_board_reset (game->priv->board, game->priv->stack);
ovcc_game_set_state (game, OVCC_GAME_STATE_STARTED);
}
return GAME_IS_STARTED (game);
}
/**
* ovcc_game_abort:
* @game: An #OVCCGame
*
* Aborts @game.
*/
void
ovcc_game_abort (OVCCGame *game)
{
g_return_if_fail (OVCC_IS_GAME (game));
if (GAME_IS_STARTED (game)) {
ovcc_game_set_state (game, OVCC_GAME_STATE_ABORTED);
}
}
/**
* ovcc_game_add_player:
* @game: An #OVCCGame
* @player: An #OVCCPlayer to add to the game
* @error: Return location for errors if any, or %NULL to ignore them.
*
* Tries to add @player to @game.
* The game must not be started.
*
* Returns: %TRUE if @player successfully joined the game, %FALSE otherwise.
*/
gboolean
ovcc_game_add_player (OVCCGame *game,
OVCCPlayer *player,
GError **error)
{
gboolean success = FALSE;
g_return_val_if_fail (OVCC_IS_GAME (game), FALSE);
if (GAME_IS_STARTED (game)) {
g_set_error (error, OVCC_GAME_ERROR, OVCC_GAME_ERROR_STARTED,
"Game is started");
} else {
GSList *item;
for (item = game->priv->players; item; item = g_slist_next (item)) {
if (item->data == player) {
g_set_error (error, OVCC_GAME_ERROR, OVCC_GAME_ERROR_PLAYER_ALREADY_ADDED,
"Player \"%s\" already on the game",
ovcc_player_get_nick (player));
break;
} else if (strcmp (ovcc_player_get_nick (player),
ovcc_player_get_nick (item->data)) == 0) {
g_set_error (error, OVCC_GAME_ERROR, OVCC_GAME_ERROR_DUPLICATED_NICK,
"There is already a player with the nick \"%s\" on the game",
ovcc_player_get_nick (player));
break;
}
}
if (item == NULL) {
game->priv->players = g_slist_append (game->priv->players,
g_object_ref (player));
game->priv->n_players ++;
g_signal_emit (game, ovcc_game_signals[SIGNAL_PLAYER_ADDED], 0, player);
success = TRUE;
}
}
return success;
}
/**
* ovcc_game_remove_player:
* @game: An #OVCCGame
* @player: An #OVCCPlayer to remove from the game
*
* Tries to remove @player from @game.
*
* Returns: %TRUE if @player successfully left the game, %FALSE otherwise.
*/
gboolean
ovcc_game_remove_player (OVCCGame *game,
OVCCPlayer *player)
{
gboolean success = FALSE;
GSList *item;
g_return_val_if_fail (OVCC_IS_GAME (game), FALSE);
/* can a player leave a running game? */
item = g_slist_find (game->priv->players, player);
if (! item) {
g_warning ("Player is not on this game");
} else {
game->priv->players = g_slist_remove_link (game->priv->players, item);
game->priv->n_players --;
g_signal_emit (game, ovcc_game_signals[SIGNAL_PLAYER_REMOVED], 0, item->data);
g_object_unref (item->data);
g_slist_free_1 (item);
success = TRUE;
}
return success;
}
/* change player to the next one */
static void
next_player (OVCCGame *game)
{
GSList *item;
item = g_slist_find (game->priv->players, game->priv->current_player);
game->priv->current_player = item->next
? item->next->data
: game->priv->players->data;
g_object_notify (G_OBJECT (game), "current-player");
}
/**
* ovcc_game_place_tile:
* @game: An #OVCCGame
* @player: The #OVCCPlayer that places the tile
* @pos: The position at which place the current tile.
*
* Tries to place the current tile.
* To place the current tile, the position bust be valid and the player must be
* the one of which it is the turn to play.
*
* Returns: %TRUE if the tile was correctly placed, %FALSE otherwise.
*/
gboolean
ovcc_game_place_tile (OVCCGame *game,
OVCCPlayer *player,
const OVCCPosition *pos)
{
gboolean placed = FALSE;
g_return_val_if_fail (OVCC_IS_GAME (game), FALSE);
g_return_val_if_fail (GAME_IS_STARTED (game), FALSE);
if (GAME_IS_STARTED (game) && IS_CURRENT_PLAYER (game, player)) {
OVCCTile *tile;
tile = ovcc_stack_peek (game->priv->stack);
if (ovcc_board_add_tile (game->priv->board, tile, pos)) {
g_object_unref (ovcc_stack_pop (game->priv->stack));
next_player (game);
placed = TRUE;
}
g_object_unref (tile);
}
return placed;
}
/**
* ovcc_game_get_current_tile:
* @game: An #OVCCGame
*
* Gets the current tile to be placed.
*
* Returns: The tile to be placed, or %NULL if none are left.
*/
OVCCTile *
ovcc_game_get_current_tile (const OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), NULL);
return ovcc_stack_peek (game->priv->stack);
}
/**
* ovcc_game_get_board:
* @game: An #OVCCGame
*
* Gets the board used by @game.
*
* Returns: The game's board
*/
OVCCBoard *
ovcc_game_get_board (const OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), NULL);
return game->priv->board;
}
/**
* ovcc_game_get_current_player:
* @game: An #OVCCGame
*
* Gets the player to which it is the play turn on @game
*
* Returns: The current player of @game, or %NULL if none.
*/
OVCCPlayer *
ovcc_game_get_current_player (const OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), NULL);
return game->priv->current_player;
}
/**
* ovcc_game_get_state:
* @game: An #OVCCGame
*
* Gets the state of an #OVCCGame.
*
* Returns: The current state of @game
*/
OVCCGameState
ovcc_game_get_state (const OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), 0);
return game->priv->state;
}
/**
* ovcc_game_get_n_tiles_left:
* @game: An #OVCCGame
*
* Gets the current count of tiles left in the game's stack.
*
* Returns: The number of tiles left to be placed.
*/
guint
ovcc_game_get_n_tiles_left (const OVCCGame *game)
{
g_return_val_if_fail (OVCC_IS_GAME (game), 0);
return ovcc_stack_size (game->priv->stack);
}
/*
*
* Copyright (C) 2009 Colomban Wendling <ban@herbesfolles.org>
* Jonathan Michalon <studios.chalmion@no-log.org>
*
* 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 3 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