Commit 6b3d5b5e authored by Colomban Wendling's avatar Colomban Wendling

Port TileSet to Vala

parent 1a09bff8
......@@ -11,14 +11,13 @@ libovcc_la_SOURCES = enumtypes.c \
stack.vala \
tile.vala \
tileobject.vala \
tileset.c \
tileset.vala \
tilesdef.vala \
utils.vala \
xmlutils.c
ovccinclude_HEADERS = ovcc.h \
game.h \
player.h \
tileset.h
player.h
test_LDADD = libovcc.la -lpthread
test_SOURCES = test.c
......
......@@ -20,19 +20,6 @@
namespace OVCC
{
public class TileSet : Object
{
public delegate void TileSetForach (TileSet ts, Tile t, int count);
public Tile first {get; set; }
public void foreach (TileSetForach f)
{
}
}
public class Stack : Object
{
private Queue<Tile> _stack = new Queue<Tile> ();
......@@ -118,6 +105,7 @@ namespace OVCC
for (var i = 0; i < count; i++) {
this.add (t);
}
return true;
});
if (ts.first == null) {
critical ("Given set has no first tile");
......
/*
*
* 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 "tileset.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string.h>
#include "typeutils.h"
#include "xmlutils.h"
#include "tilesdef.h"
#include "tile.h"
/**
* SECTION: tileset
* @short_description: Managment of aggregations of tiles to make tilesets
* @see_also: #OVCCTile, #OVCCTilesDef
*
* This module manages Tile Sets. Tile Sets are list of tiles and the number of
* them in the set.
* Tile Sets works together with tiles definitions to provide a set of tiles.
*
* Tile Sets are represented by #OVCCTileSet objects.
*
* A Tile Set is created with ovcc_tileset_new() and freed using
* ovcc_tileset_unref(). They can be loaded from XML definitions with
* ovcc_tileset_load();
* Adding a tile to a Tile Set is done using ovcc_tileset_add(),
* removing one is done using ovcc_tileset_remove(), and removing all tiles from
* a set is done using ovcc_tileset_clear().
*
* <example>
* <title>Simple example showing the load of a Tile Set from an URI</title>
* <programlisting>
* #include <glib.h>
* #include <gio/gio.h>
* #include "tileset.h"
*
* OVCCTileSet *
* load_our_tileset (const gchar *uri,
* OVCCTilesDef *available_tiles)
* {
* OVCCTileSet *set;
* GFile *file;
* GError *err = NULL;
*
* file = g_file_new_for_uri (uri);
* set = ovcc_tileset_new ();
* if (! ovcc_tileset_load (set, available_tiles, file, &err)) {
* g_critical ("Failed to load Tile Set: %s", err->message);
* g_error_free (err);
* // Return NULL if the tile set cannot be loaded
* ovcc_tileset_unref (set), set = NULL;
* }
* g_object_unref (file);
*
* return set;
* }
* </programlisting>
* </example>
*
* <example>
* <title>Example of a tileset XML file</title>
* <programlisting>
* <![CDATA[
* <?xml version="1.0" encoding="utf8"?>
* <!DOCTYPE tileset SYSTEM "tileset.dtd">
* <tileset>
* <!-- The first tile is a #1 -->
* <first id="t1" />
*
* <!-- Tile #1 appears 12 times in the set -->
* <tile id="t1" count="12" />
* <!-- Tile #2 appreas 2 times in the set -->
* <tile id="t2" count="2" />
* </tileset>
* ]]>
* </programlisting>
* </example>
*/
struct _OVCCTileSetEntry
{
OVCCTile *tile; /* Pointer to the tile */
guint count; /* number of occurrences of this tile */
};
/**
* OVCCTileSet:
*
* The opaque object representing a set of tiles
*/
struct _OVCCTileSet
{
gint ref_count;
gchar *name;
GHashTable *tiles;
OVCCTile *first;
};
typedef struct _OVCCTileSetEntry OVCCTileSetEntry;
/* Creates a new set's entry */
static OVCCTileSetEntry *
entry_new (OVCCTile *tile,
guint count)
{
OVCCTileSetEntry *entry;
entry = g_slice_alloc (sizeof *entry);
if (entry) {
entry->tile = g_object_ref (tile);
entry->count = count;
}
return entry;
}
/* Frees a set's entry */
static void
entry_free (OVCCTileSetEntry *entry)
{
if (entry) {
/*g_debug ("%.2u x #%.3u", entry->count, ovcc_tile_get_id (entry->tile));*/
g_object_unref (entry->tile);
g_slice_free1 (sizeof *entry, entry);
}
}
/* Use ref and unref to make it a singleton */
DEFINE_BOXED_TYPE (OVCCTileSet, ovcc_tileset,
ovcc_tileset_ref, ovcc_tileset_unref)
/**
* ovcc_tileset_error_quark:
*
* Gets the error domain quark for #OVCCTileSet errors.
*
* Returns: The #OVCCTileSet's error domain quark.
*/
GQuark
ovcc_tileset_error_quark (void)
{
static GQuark error_quark = 0;
if (G_UNLIKELY (error_quark == 0)) {
error_quark = g_quark_from_static_string ("OVCCTileSetError");
}
return error_quark;
}
/**
* ovcc_tileset_new:
*
* Creates a new TileSet.
*
* Returns: The newly created #OVCCTileSet.
*/
OVCCTileSet *
ovcc_tileset_new (void)
{
OVCCTileSet *set;
set = g_malloc (sizeof *set);
if (set) {
set->ref_count = 1;
set->name = NULL;
set->first = NULL;
set->tiles = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)entry_free);
}
return set;
}
/**
* ovcc_tileset_ref:
* @set: A valid #OVCCTileSet
*
* Increases reference count of @set.
*
* Returns: @set.
*/
OVCCTileSet *
ovcc_tileset_ref (OVCCTileSet *set)
{
g_atomic_int_inc (&set->ref_count);
return set;
}
/**
* ovcc_tileset_unref:
* @set: A valid #OVCCTileSet
*
* Decreases reference count of @set and frees it if count drops to 0.
*/
void
ovcc_tileset_unref (OVCCTileSet *set)
{
if (g_atomic_int_dec_and_test (&set->ref_count)) {
g_hash_table_destroy (set->tiles);
g_object_unref (set->first);
g_free (set->name);
g_free (set);
}
}
/**
* ovcc_tileset_set_name:
* @set: An #OVCCTileSet.
* @name: The new name of the tileset.
*
* Sets the name of a tileset.
*/
void
ovcc_tileset_set_name (OVCCTileSet *set,
const gchar *name)
{
g_free (set->name);
set->name = g_strdup (name);
}
/**
* ovcc_tileset_get_name:
* @set: An #OVCCTileSet.
*
* Gives the name of a tileset.
*
* Returns: The name of the tileset, or NULL it it has no name.
*/
const gchar *
ovcc_tileset_get_name (OVCCTileSet *set)
{
return set->name;
}
/**
* ovcc_tileset_set_first:
* @set: An #OVCCTileSet
* @tile: The first tile
*
* Sets the first tile of a set. The first tile is not port of the other tiles
* and is used as the initial tile around which the other can be played.
*/
void
ovcc_tileset_set_first (OVCCTileSet *set,
OVCCTile *tile)
{
if (set->first) {
g_object_unref (set->first);
}
set->first = g_object_ref (tile);
}
/**
* ovcc_tileset_get_first:
* @set: An #OVCCTileSet
*
* Gets the default tile.
*
* Returns: The first tile.
*/
OVCCTile *
ovcc_tileset_get_first (const OVCCTileSet *set)
{
return set->first;
}
/**
* ovcc_tileset_clear:
* @set: An #OVCCTileSet to clear.
*
* Removes all tiles in the given tile set.
*/
void
ovcc_tileset_clear (OVCCTileSet *set)
{
g_hash_table_remove_all (set->tiles);
}
/**
* ovcc_tileset_remove:
* @set: An #OVCCTileSet.
* @id: The ID of the tile to remove from the set.
*
* Removes a tile from a tileset.
*
* Returns: %TRUE if the tile was removed, %FALSE if it wasn't found in the set.
*/
gboolean
ovcc_tileset_remove (OVCCTileSet *set,
OVCCTileID id)
{
return g_hash_table_remove (set->tiles, GINT_TO_POINTER (id));
}
/**
* ovcc_tileset_has_tile:
* @set: An #OVCCTileSet.
* @id: The ID of the tile to search for.
*
* Says whether a tile is in a set.
*
* Returns: %TRUE if the tile was found in the set, %FALSE otherwise.
*/
gboolean
ovcc_tileset_has_tile (OVCCTileSet *set,
OVCCTileID id)
{
return g_hash_table_lookup (set->tiles, GINT_TO_POINTER (id)) != NULL;
}
/**
* ovcc_tileset_is_empty:
* @set: An #OVCCTileSet.
*
* Says whether a tileset contains any tile.
*
* Returns: %TRUE it the tileset is empty, %FALSE otherwise.
*/
gboolean
ovcc_tileset_is_empty (OVCCTileSet *set)
{
return ovcc_tileset_size (set) == 0;
}
/**
* ovcc_tileset_size:
* @set: An #OVCCTileSet.
*
* Gives the number of tiles in a tileset.
*
* Returns: The number of tiles in the TileSet.
*/
guint
ovcc_tileset_size (OVCCTileSet *set)
{
return g_hash_table_size (set->tiles);
}
/**
* ovcc_tileset_add:
* @set: An #OVCCTileSet.
* @tile: The #OVCCTile to add.
* @count: The number of @tile in the set.
*
* Adds a Tile to a TileSet.
*
* <note>
* A reference is added to the tile.
* </note>
*/
void
ovcc_tileset_add (OVCCTileSet *set,
OVCCTile *tile,
guint count)
{
OVCCTileSetEntry *entry;
entry = entry_new (tile, count);
g_hash_table_insert (set->tiles, GINT_TO_POINTER (ovcc_tile_get_id (tile)),
entry);
}
struct CBForeachData {
OVCCTileSet *set;
OVCCTileSetForeachFunc func;
gpointer data;
gboolean keep_doing;
};
static void
hash_table_foreach_cb (gpointer key G_GNUC_UNUSED,
gpointer value,
gpointer data)
{
struct CBForeachData *fdata = data;
if (fdata->keep_doing) {
OVCCTileSetEntry *entry = value;
fdata->keep_doing = fdata->func (fdata->set, entry->tile, entry->count,
fdata->data);
}
}
/**
* ovcc_tileset_foreach:
* @set: An #OVCCTileSet.
* @func: A function to call on each element of the tileset.
* @data: Arbitrary data to pass to @func as data parameter.
*
* Calls a function on each element of a tileset.
*/
void
ovcc_tileset_foreach (OVCCTileSet *set,
OVCCTileSetForeachFunc func,
gpointer data)
{
struct CBForeachData fdata;
fdata.set = set;
fdata.func = func;
fdata.data = data;
fdata.keep_doing = TRUE;
g_hash_table_foreach (set->tiles, hash_table_foreach_cb, &fdata);
}
/* FAKE FUNCTION */
static void
print_element_names (xmlNode *a_node, guint d)
{
xmlNode *cur_node;
for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
xmlAttr *cur_attr;
printf ("%*s%s\n", d * 2, "", cur_node->name);
for (cur_attr = cur_node->properties; cur_attr; cur_attr = cur_attr->next) {
printf ("%*s%s:%s = %s\n", d * 2, "", cur_node->name, cur_attr->name, cur_attr->children->content);
}
}
print_element_names (cur_node->children, d + 1);
}
}
#define TS_TILESET ((const xmlChar *)"tileset")
#define TS_TILESET_NAME ((const xmlChar *)"name")
#define TS_FIRST ((const xmlChar *)"first")
#define TS_FIRST_ID ((const xmlChar *)"id")
#define TS_TILE ((const xmlChar *)"tile")
#define TS_TILE_ID ((const xmlChar *)"id")
#define TS_TILE_COUNT ((const xmlChar *)"count")
static gboolean
read_tile (xmlNode *node,
OVCCTileSet *set,
OVCCTilesDef *tiles,
GError **error)
{
xmlAttr *cur_attr;
gboolean success = TRUE;
OVCCTileID id = 0;
guint count = 0;
/* read attributes */
for (cur_attr = node->properties; success && cur_attr; cur_attr = cur_attr->next) {
if (cur_attr->type == XML_ATTRIBUTE_NODE) {
/* tile ID (t[0-9]+) */
if (xmlStrcmp (TS_TILE_ID, cur_attr->name) == 0) {
const gchar *nptr = (const gchar *)cur_attr->children->content;
GError *err = NULL;
id = ovcc_xmlutils_read_id (nptr, "t", &err);
if (err) {
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
"[%s:%u] Invalid value '%s' for attribute '%s' of element <%s>: %s",
node->doc->URL, node->line, nptr, cur_attr->name, node->name,
err->message);
g_error_free (err);
success = FALSE;
}
} else if (xmlStrcmp (TS_TILE_COUNT, cur_attr->name) == 0) {
const gchar *nptr = (const gchar *)cur_attr->children->content;
GError *err = NULL;
count = ovcc_xmlutils_read_uint (nptr, &err);
if (err) {
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
"[%s:%u] Invalid value '%s' for attribute '%s' of element <%s>: %s",
node->doc->URL, node->line, nptr, cur_attr->name, node->name,
err->message);
g_error_free (err);
success = FALSE;
}
if (count <= 0) {
g_debug ("What a stupid tile count: %u (for tile id #%u)", count, id);
}
}
#if 0
else {
/* not sure it is good to check the validity here as some attributes
* may be there even if they are not semcific to the tag, but more
* XML-generic like the xml:lang attribute. */
if (xmlStrcmp (cur_attr->ns->prefix, (const xmlChar *)"xml") != 0) {
g_warning ("Malformed file! tile element <%s> have no attribute %s",
node->name, cur_attr->name);
}
break;
}
#endif
}
}
/* tile can have no children */
if (success) {
/* if these tiles are the first one, don't load the first again.
* this supposes that a tile ID appears only once in a tileset. */
if (ovcc_tile_get_id (set->first) == id) {
count --;
}
if (count > 0) {
OVCCTile *tile;
tile = ovcc_tilesdef_get_tile (tiles, id);
if (! tile) {
g_set_error (error, OVCC_TILESET_ERROR, OVCC_TILESET_ERROR_MISSING_TILE,
"Tile ID %u not found in the tiles definitions", id);
success = FALSE;
} else {
ovcc_tileset_add (set, tile, count);
}
}
}
return success;
}
static gboolean
read_first (xmlNode *node,
OVCCTileSet *set,
OVCCTilesDef *tiles,
GError **error)
{
xmlAttr *cur_attr;
gboolean success = TRUE;
OVCCTileID id = 0;
for (cur_attr = node->properties; success && cur_attr; cur_attr = cur_attr->next) {
if (cur_attr->type == XML_ATTRIBUTE_NODE) {
/* tile ID (t[0-9]+) */
if (xmlStrcmp (TS_FIRST_ID, cur_attr->name) == 0) {
const gchar *nptr = (const gchar *)cur_attr->children->content;
GError *err = NULL;
id = ovcc_xmlutils_read_id (nptr, "t", &err);
if (err) {
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
"[%s:%u] Invalid value '%s' for attribute '%s' of element <%s>: %s",
node->doc->URL, node->line, nptr, cur_attr->name, node->name,
err->message);
g_error_free (err);
success = FALSE;
}
}
}
}
if (success) {
OVCCTile *tile;
/* g_debug ("first tile is: %u", id); */
tile = ovcc_tilesdef_get_tile (tiles, id);
if (! tile) {
g_set_error (error, OVCC_TILESET_ERROR, OVCC_TILESET_ERROR_MISSING_TILE,
"Tile ID %u not found in the tiles definitions", id);
success = FALSE;
} else {
ovcc_tileset_set_first (set, tile);
}
}
return success;
}
static gboolean