#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+
#include <gtk/gtk.h>
#include "grr_board_view.h"
+#include "grr_util.h"
#include "args.h"
typedef struct grr_game {
GtkWidget *window;
GtkWidget *board_view;
+ GtkWidget *command_entry;
+
GtkTextBuffer *message_buffer;
GtkWidget *message_view;
- GtkWidget *command_entry;
- GtkTextIter message_iter;
+ int msg_id;
+ int last_move_msg_id;
+ rr_robot_t last_move_robot;
} grr_game_t;
+static int
+grr_game_printf (grr_game_t *game, const char *fmt, ...);
+
+static int
+grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap);
+
+static int
+grr_game_print (grr_game_t *game, const char *msg);
+
static int
grr_game_start_gui (grr_game_t *game);
}
game.board = rr_board_create (16, 16);
+ game.msg_id = 0;
+ game.last_move_msg_id = 0;
+ game.last_move_robot = RR_ROBOT_NONE;
source = grr_game_notices_source_new (&game);
g_source_set_priority (source, GDK_PRIORITY_EVENTS);
{
rr_board_t *board = game->board;
rr_status_t status;
- char **notice_s;
rr_notice_t *notice;
- int i;
while (rr_client_notice_pending (game->client)) {
- status = rr_client_next_notice (game->client, ¬ice_s);
+ status = rr_client_next_notice (game->client, ¬ice);
if (status) {
- fprintf (stderr, "Error during rr_client_notice: %s\n",
+ fprintf (stderr, "Error during rr_client_next_notice: %s\n",
rr_status_str (status));
- gtk_exit (1);
- return;
- }
- for (i=0; notice_s[i]; i++) {
- gtk_text_buffer_insert_at_cursor (game->message_buffer, notice_s[i], -1);
- gtk_text_buffer_insert_at_cursor (game->message_buffer, " ", -1);
+ exit (1);
}
- gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
- gtk_text_buffer_get_iter_at_offset (game->message_buffer,
- &game->message_iter,
- -1);
- gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (game->message_view),
- &game->message_iter,
- 0.0, FALSE, 0.0, 0.0);
-
- notice = rr_client_parse_notice (game->client, notice_s);
- if (notice == NULL) {
- int i;
- fprintf (stderr, "Failed to parse notice: ");
- for (i=0; notice_s[i]; i++)
- fprintf (stderr, " %s", notice_s[i]);
- fprintf (stderr, "\n");
- return;
+ if (!notice) {
+ fprintf (stderr, "Missing notice\n");
+ continue;
}
switch (notice->type) {
case RR_NOTICE_USER:
+ grr_game_printf (game,
+ "\nGLOBAL: %s has connected.", notice->u.string);
+ break;
case RR_NOTICE_QUIT:
+ grr_game_printf (game,
+ "\nGLOBAL: %s has disconnected.", notice->u.string);
+ break;
case RR_NOTICE_GAME:
+ grr_game_printf (game,
+ "\nGLOBAL: New game available: %s.", notice->u.string);
+ break;
case RR_NOTICE_DISPOSE:
+ grr_game_printf (game,
+ "\nGLOBAL: Game %s has been disposed.", notice->u.string);
+ break;
case RR_NOTICE_MESSAGE:
+ grr_game_printf (game, "\n%s> %s",
+ notice->u.message.username,
+ notice->u.message.text);
+ break;
case RR_NOTICE_GAMESTATE:
+ grr_game_printf (game, "\nGame state changed to: %s.",
+ rr_gamestate_str (notice->u.gamestate));
+ break;
+ case RR_NOTICE_TURN:
+ grr_game_print (game, "\nNew round!");
+ rr_board_set_goal_target (board, notice->u.target);
+ gtk_widget_queue_draw (GTK_WIDGET (game->window));
+ break;
+ case RR_NOTICE_GAMEOVER:
+ {
+ char *diagram;
+ grr_game_printf (game, "\nGame over. New game will begin now.");
+ /* XXX: Can drop this when the BOARD NOTICE is added in the server */
+ rr_client_show (game->client, &diagram);
+ rr_board_parse (board, diagram);
+ free (diagram);
+ gtk_widget_queue_draw (GTK_WIDGET (game->window));
+ }
+ break;
case RR_NOTICE_JOIN:
+ grr_game_printf (game, "\nUser %s has joined the game.",
+ notice->u.string);
+ break;
case RR_NOTICE_WATCH:
+ grr_game_printf (game, "\nUser %s has started watching the game.",
+ notice->u.string);
+ break;
case RR_NOTICE_PART:
+ grr_game_printf (game, "\nUser %s has left the game.",
+ notice->u.string);
+ break;
case RR_NOTICE_BID:
+ grr_game_printf (game, "\nUser %s bids %d.",
+ notice->u.bid.username,
+ notice->u.bid.number);
+ break;
case RR_NOTICE_REVOKE:
- case RR_NOTICE_TIMER:
+ grr_game_printf (game, "\nUser %s revokes previous bid.",
+ notice->u.string);
+ break;
case RR_NOTICE_ABANDON:
+ grr_game_printf (game, "\nUser %s is willing to abandon this turn.",
+ notice->u.string);
+ break;
case RR_NOTICE_NOBID:
- case RR_NOTICE_ACTIVE:
+ grr_game_printf (game, "\nUser %s sees no point in bidding further this round",
+ notice->u.string);
+ break;
case RR_NOTICE_MOVE:
+ if (game->msg_id == game->last_move_msg_id
+ && game->last_move_robot == notice->u.move.robot) {
+ game->last_move_msg_id = grr_game_printf (game, " %s",
+ rr_direction_str (notice->u.move.direction));
+ } else {
+ game->last_move_msg_id = grr_game_printf (game, "\nMove #%d: %s %s",
+ notice->u.move.count,
+ rr_robot_str (notice->u.move.robot),
+ rr_direction_str (notice->u.move.direction));
+ game->last_move_robot = notice->u.move.robot;
+ }
+ break;
case RR_NOTICE_UNDO:
+ grr_game_print (game, "\nUNDO");
+ break;
case RR_NOTICE_RESET:
+ grr_game_print (game, "\nRESET");
+ break;
case RR_NOTICE_SCORE:
+ grr_game_printf (game, "\nScore for %s: %d.",
+ notice->u.bid.username,
+ notice->u.bid.number);
+ break;
case RR_NOTICE_ACTIVATE:
- /* XXX: Need to actually handle many of these. */
- fprintf (stderr, "Warning: Ignoring notice of type %d\n", notice->type);
+ grr_game_printf (game, "\nYour turn.",
+ notice->u.number);
break;
- case RR_NOTICE_POSITION:
- rr_board_position (board, notice->u.position.robot,
- notice->u.position.x, notice->u.position.y);
- gtk_widget_queue_draw (GTK_WIDGET (game->window));
+ case RR_NOTICE_ACTIVE:
+ grr_game_printf (game, "\nUser %s now active to demonstrate solution in %d moves.",
+ notice->u.bid.username,
+ notice->u.bid.number);
break;
- case RR_NOTICE_TURN:
- rr_board_set_goal_target (board, notice->u.target);
+ case RR_NOTICE_TIMER:
+ grr_game_printf (game, "\nTime remaining: %d seconds.",
+ notice->u.number);
+ break;
+ case RR_NOTICE_POSITION:
+ rr_board_position_robot (board, notice->u.position.robot,
+ notice->u.position.x, notice->u.position.y);
gtk_widget_queue_draw (GTK_WIDGET (game->window));
break;
}
status = rr_client_request (game->client, entry_text, &response);
if (status) {
- gtk_text_buffer_insert_at_cursor (game->message_buffer,
- "ERROR: ", -1);
- gtk_text_buffer_insert_at_cursor (game->message_buffer,
- rr_status_str (status), -1);
- gtk_text_buffer_insert_at_cursor (game->message_buffer,
- "\n", -1);
+ grr_game_printf (game, "\nERROR: %s.", rr_status_str (status));
} else {
- for (i=0; response[i]; i++) {
- gtk_text_buffer_insert_at_cursor (game->message_buffer,
- response[i], -1);
- gtk_text_buffer_insert_at_cursor (game->message_buffer,
- " ", -1);
+ if (response[0]) {
+ grr_game_print (game, "\n");
+ for (i=0; response[i]; i++)
+ grr_game_printf (game, "%s ", response[i]);
}
- gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
}
gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
}
+static int
+grr_game_printf (grr_game_t *game, const char *fmt, ...)
+{
+ int msg_id;
+
+ va_list ap;
+
+ va_start (ap, fmt);
+ msg_id = grr_game_vprintf (game, fmt, ap);
+ va_end (ap);
+
+ return msg_id;
+}
+
+static int
+grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap)
+{
+ char *msg;
+ int msg_id;
+
+ grr_sprintf_alloc_va (&msg, fmt, ap);
+ if (msg == NULL)
+ return 0;
+
+ msg_id = grr_game_print (game, msg);
+
+ free (msg);
+
+ return msg_id;
+}
+
+static int
+grr_game_print (grr_game_t *game, const char *msg)
+{
+ GtkTextIter iter;
+ GtkTextMark *mark;
+
+ /* There might be a lighter way to do this, but this seems to
+ work. */
+
+ gtk_text_buffer_get_end_iter (game->message_buffer, &iter);
+
+ mark = gtk_text_buffer_get_mark (game->message_buffer, "end");
+
+ gtk_text_buffer_move_mark (game->message_buffer, mark, &iter);
+
+ gtk_text_buffer_insert (game->message_buffer, &iter, msg, -1);
+
+ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (game->message_view),
+ mark, 0.0, TRUE, 0.0, 1.0);
+
+ return ++game->msg_id;
+}
+
static int
grr_game_start_gui (grr_game_t *game)
{
vbox = gtk_vbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (window), vbox);
{
+ GtkTextIter iter;
game->board_view = grr_board_view_new (game->board);
grr_board_view_set_client (GRR_BOARD_VIEW (game->board_view), game->client);
game->message_buffer = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_get_iter_at_offset (game->message_buffer,
+ &iter, -1);
+ gtk_text_buffer_create_mark (game->message_buffer,
+ "end", &iter, FALSE);
game->message_view = gtk_text_view_new_with_buffer (game->message_buffer);
game->command_entry = gtk_entry_new ();
gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
gtk_box_pack_start (GTK_BOX (vbox), vpaned,
TRUE, TRUE, 0);
- gtk_container_add (GTK_CONTAINER (vbox), vpaned);
{
board_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
gtk_paned_pack1 (GTK_PANED (vpaned), board_frame, TRUE, TRUE);