diff --git a/ds_pof.c b/ds_pof.c new file mode 100644 index 0000000..a9b63a7 --- /dev/null +++ b/ds_pof.c @@ -0,0 +1,493 @@ +// gcc $(pkg-config --cflags gtk4) -o ds_pof ds_pof.c $(pkg-config --libs gtk4) + +#include + +// --------------------------------------------------------- +// global widgets and buffers needed across various +// callback functons //TODO: make this local to app and pass as struct +// --------------------------------------------------------- +static GtkWidget *str_button; +static GtkWidget *str_modifier; + +static GtkWidget *dex_button; +static GtkWidget *dex_modifier; + +static GtkWidget *int_button; +static GtkWidget *int_modifier; + +static GtkWidget *con_button; +static GtkWidget *con_modifier; + +static GtkWidget *wis_button; +static GtkWidget *wis_modifier; + +static GtkWidget *cha_button; +static GtkWidget *cha_modifier; + +static char modifier_buf[20] = {'\0'}; +static char result_buf[200] = {'\0'}; + +static GtkWidget *prof_button; +static GtkEntryBuffer *dice_roll_buffer; +static GtkTextBuffer *calc_result_buffer; +static GtkWidget* calc_result_diag; + +static GtkWidget *cinder_wrath_check; + +// --------------------------------------------------------- +// callback function to calculate attack roll whenever the +// attack button is pressed +// --------------------------------------------------------- +static void attack_calc(GtkWidget *widget) +{ + //TODO: need error handling or some kind of integer only buffer (maybe spin button without buttons?) + int dice_roll_value = atoi(gtk_entry_buffer_get_text(dice_roll_buffer)); + int str_mod_value = atoi(gtk_label_get_text((GtkLabel *) str_modifier)); + int prof_value = gtk_spin_button_get_value_as_int((GtkSpinButton *) prof_button); + int atk_result = dice_roll_value + prof_value + str_mod_value; + sprintf(result_buf, "Attack result: %d [Dice: %d] [Proficiency: %d] [Strength: %d]", + atk_result, dice_roll_value, prof_value, str_mod_value); + + gtk_text_buffer_set_text(calc_result_buffer, result_buf, -1); + + // clear the dice roll field for convenience + gtk_entry_buffer_delete_text(dice_roll_buffer, 0, -1 /*delete all*/); +} + +// --------------------------------------------------------- +// callback function to calculate damage roll whenever the +// damage button is pressed +// --------------------------------------------------------- +static void damage_calc(GtkWidget *widget) +{ + int dice_roll_value = atoi(gtk_entry_buffer_get_text(dice_roll_buffer)); + int cinder_wrath = gtk_check_button_get_active((GtkCheckButton *) cinder_wrath_check); + int str_mod_value = atoi(gtk_label_get_text((GtkLabel *) str_modifier)); + int prof_value = (cinder_wrath == 1) ? gtk_spin_button_get_value_as_int((GtkSpinButton *) prof_button) : 0; + int additional_buff = 2; //TODO: this is for duelling, need a way to parameteris this/configure + + int dmg_result = dice_roll_value + prof_value + str_mod_value + additional_buff; + sprintf(result_buf, "Damage result: %d [Dice: %d] [Proficiency: %d | Cinder Wrath %s] [Strength: %d] [Duelling Buff: %d]", + dmg_result, dice_roll_value, prof_value, (cinder_wrath == 1) ? "Enabled" : "Disabled", + str_mod_value, additional_buff); + + gtk_text_buffer_set_text(calc_result_buffer, result_buf, -1); + + // clear the dice roll field for convenience + gtk_entry_buffer_delete_text(dice_roll_buffer, 0, -1 /*delete all*/); +} + + +// --------------------------------------------------------- +// callback function used by all core stat spin-button +// to update a stat modifier whenever the raw value in +// the spin-button is changed +// --------------------------------------------------------- +static int stat_calc(GtkWidget *spin_button, GtkWidget *mod_label) +{ + int raw_value = gtk_spin_button_get_value_as_int((GtkSpinButton *) spin_button); + int mod_value = (int) ((raw_value - 10) / 2); + char mod_buf[20] = {'\0'}; + sprintf(mod_buf, "%d", mod_value); + gtk_label_set_markup(GTK_LABEL(mod_label), mod_buf); +} + +// --------------------------------------------------------- +// callback function to set the character stats to those +// parsed from an input file +// --------------------------------------------------------- +static void load_from_file_activated(GSimpleAction *action, GVariant *parameter, gpointer app) +{ + g_print("load\n"); +} + +// --------------------------------------------------------- +// callback function to save current character stats to +// a file +// --------------------------------------------------------- +static void save_to_file_activated(GSimpleAction *action, GVariant *parameter, gpointer app) +{ + g_print("save\n"); +} + +// --------------------------------------------------------- +// callback function to exit the app +// --------------------------------------------------------- +static void quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) +{ + g_application_quit(G_APPLICATION (app)); +} + +// --------------------------------------------------------- +// activation function - configures the application UI +// --------------------------------------------------------- +static void activate (GtkApplication *app, gpointer user_data) +{ + GtkWidget *window; + GtkWidget *grid; + GtkWidget *button; + GtkWidget *inscription; + GtkAdjustment *adjustment; + + /* create a new window */ + window = gtk_application_window_new(app); + gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); + + /* Here we construct the container that is going pack our core elements (buttons etc) */ + grid = gtk_grid_new (); + + // create two stacks and a switcher so we can have two tabs + // one tab is for the basic attack/damage roll function using the 6 cores stats + // the second tab is for full character customisation and can affect attack/damage roll options //TODO: implement the contents of second tab + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); + GtkWidget *stack = gtk_stack_new (); + GtkWidget *switcher = gtk_stack_switcher_new (); + + GtkWidget *label2 = gtk_label_new("Stack Page 2"); + + gtk_stack_add_titled((GtkStack *) stack, GTK_WIDGET(grid), "Calculator", "Calculator"); + gtk_stack_add_titled((GtkStack *) stack, GTK_WIDGET(label2), "Character Details", "Character Details"); + + gtk_stack_switcher_set_stack((GtkStackSwitcher *) switcher, (GtkStack *) stack); + + //order matters, this way around gives tabs at top, invert for tabs at bottom + gtk_box_prepend((GtkBox *) box, stack); + gtk_box_prepend((GtkBox *) box, switcher); + + gtk_window_set_child(GTK_WINDOW(window), box); + + // --------------------------------------------------------- + // Setup the spin buttons (increment/decrement buttons) + // for the 6 core stats (STR, DEX, CON, INT, WIS, CHA) + // The buttons start with a default of 10, can be manually + // editied like a text box, have specified limits, and set + // step sizes. + // When the buttons are modified, the callback function + // reads the button value and calculates the stat modifier + // to then display (and use for atk/dmg calculations) + // TODO: try to condense this block + // --------------------------------------------------------- + + // label and increment/decrement button for strength + const char *strength = "STR"; + GtkWidget *label = gtk_label_new (NULL); + gtk_label_set_markup(GTK_LABEL (label), strength); + gtk_grid_attach(GTK_GRID (grid), label, 0, 1, 1, 1); //TODO: we can raise these up by one + + adjustment = gtk_adjustment_new(10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + str_button = gtk_spin_button_new(adjustment, 1.0, 0); + gtk_grid_attach(GTK_GRID (grid), str_button, 0, 2, 1, 1); + + str_modifier = gtk_label_new (NULL); + gtk_grid_attach(GTK_GRID (grid), str_modifier, 0, 3, 1, 1); + + g_signal_connect(str_button, "value-changed", G_CALLBACK(stat_calc), str_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(str_button, str_modifier); + + // label and increment/decrement button for dexterity + const char *dexterity = "DEX"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), dexterity); + gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1); + + adjustment = gtk_adjustment_new (10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + dex_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), dex_button, 1, 2, 1, 1); + + dex_modifier = gtk_label_new (NULL); + gtk_grid_attach(GTK_GRID (grid), dex_modifier, 1, 3, 1, 1); + + g_signal_connect(dex_button, "value-changed", G_CALLBACK(stat_calc), dex_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(dex_button, dex_modifier); + + // label and increment/decrement button for constitution + const char *constitution = "CON"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), constitution); + gtk_grid_attach (GTK_GRID (grid), label, 2, 1, 1, 1); + + adjustment = gtk_adjustment_new (10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + con_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), con_button, 2, 2, 1, 1); + + con_modifier = gtk_label_new (NULL); + gtk_grid_attach(GTK_GRID (grid), con_modifier, 2, 3, 1, 1); + + g_signal_connect(con_button, "value-changed", G_CALLBACK(stat_calc), con_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(con_button, con_modifier); + + // label and increment/decrement button for intelligence + const char *intelligence = "INT"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), intelligence); + gtk_grid_attach (GTK_GRID (grid), label, 3, 1, 1, 1); + + adjustment = gtk_adjustment_new (10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + int_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), int_button, 3, 2, 1, 1); + + int_modifier = gtk_label_new (NULL); + gtk_grid_attach(GTK_GRID (grid), int_modifier, 3, 3, 1, 1); + + g_signal_connect(int_button, "value-changed", G_CALLBACK(stat_calc), int_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(int_button, int_modifier); + + // label and increment/decrement button for wisdom + const char *wisdom = "WIS"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), wisdom); + gtk_grid_attach (GTK_GRID (grid), label, 4, 1, 1, 1); + + adjustment = gtk_adjustment_new (10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + wis_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), wis_button, 4, 2, 1, 1); + + wis_modifier = gtk_label_new (NULL); + gtk_grid_attach(GTK_GRID (grid), wis_modifier, 4, 3, 1, 1); + + g_signal_connect(wis_button, "value-changed", G_CALLBACK(stat_calc), wis_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(wis_button, wis_modifier); + + // label and increment/decrement button for charisma + const char *charisma = "CHA"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), charisma); + gtk_grid_attach (GTK_GRID (grid), label, 5, 1, 1, 1); + + adjustment = gtk_adjustment_new (10.0, 0.0, 100.0, 1.0, 5.0, 0.0); + cha_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), cha_button, 5, 2, 1, 1); + + cha_modifier = gtk_label_new(NULL); + gtk_grid_attach(GTK_GRID (grid), cha_modifier, 5, 3, 1, 1); + + g_signal_connect(cha_button, "value-changed", G_CALLBACK(stat_calc), cha_modifier); + + // perform first-time stat calculation so something exists in the modifier fields + stat_calc(cha_button, cha_modifier); + + // --------------------------------------------------------- + // spin button for proficiency + // --------------------------------------------------------- + + // label and increment/decrement button for proficiency + const char *proficiency = "PROF"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), proficiency); + gtk_grid_attach (GTK_GRID (grid), label, 6, 1, 1, 1); + + adjustment = gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 5.0, 0.0); + prof_button = gtk_spin_button_new (adjustment, 1.0, 0); + gtk_grid_attach (GTK_GRID (grid), prof_button, 6, 2, 1, 1); + + // --------------------------------------------------------- + // text entry field for the user to input their dice roll + // --------------------------------------------------------- + + const char *dice_roll_label = "Dice Roll:"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), dice_roll_label); + gtk_grid_attach (GTK_GRID (grid), label, 0, 4, 1, 1); + + dice_roll_buffer = gtk_entry_buffer_new(NULL, -1); + GtkWidget *dice_roll_input = gtk_entry_new_with_buffer(dice_roll_buffer); + gtk_grid_attach (GTK_GRID (grid), dice_roll_input, 1, 4, 1, 1); + + // --------------------------------------------------------- + // action buttons and options + // provide buttons to calculate attack rolls and damage + // rolls based on the supplied input dice roll + // also provide option to indicate if Cinder Wrath is + // active or not, as this adds proficiency to the dmg value + // --------------------------------------------------------- + + button = gtk_button_new_with_label ("Attack"); + g_signal_connect (button, "clicked", G_CALLBACK (attack_calc), NULL); + gtk_grid_attach (GTK_GRID (grid), button, 0, 5, 1, 1); + + button = gtk_button_new_with_label ("Damage"); + g_signal_connect (button, "clicked", G_CALLBACK (damage_calc), NULL); + gtk_grid_attach (GTK_GRID (grid), button, 1, 5, 1, 1); + + // checkbox to enable if raging (cinder wrath) - adds proficiency to dmg + cinder_wrath_check = gtk_check_button_new_with_label("Cinder Wrath"); + //g_signal_connect (cinder_wrath_check, "toggled", G_CALLBACK (print_hello), NULL); + gtk_grid_attach (GTK_GRID (grid), cinder_wrath_check, 2, 5, 3, 1); + + // --------------------------------------------------------- + // Text buffer for displaying the result of the calculation + // and ensure the buffer cannot be modified by the user + // --------------------------------------------------------- + + calc_result_buffer = gtk_text_buffer_new(NULL); + calc_result_diag = gtk_text_view_new_with_buffer(calc_result_buffer); + // make it so the field cannot be edited by the user (makes the text non-input) + gtk_text_view_set_editable(GTK_TEXT_VIEW(calc_result_diag), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(calc_result_diag), FALSE); + gtk_grid_attach (GTK_GRID (grid), calc_result_diag, 0, 6, 4, 2); + + // --------------------------------------------------------- + // Create a menu button that will allow for loading/saving + // character configuration, as well as exiting the app + // The menu button sits within a header bar, and uses + // a Menu Model for the menu structure and contents + // --------------------------------------------------------- + + // the menu button that will sit in the header bar + GtkWidget *menubutton = gtk_menu_button_new(); + gtk_menu_button_set_direction((GtkMenuButton*) menubutton, GTK_ARROW_DOWN); //DOWN + //gtk_widget_set_valign (GtkWidget* widget, GtkAlign align); + gtk_widget_set_halign(menubutton, GTK_ALIGN_END); + + + // the top-level menu + GMenu *settings_menu = g_menu_new(); + + // the different options we have in the top-level menu + GMenuItem *load_file = g_menu_item_new( "Load from file", "app.load_from_file" ); + g_menu_append_item(settings_menu, load_file); + + GMenuItem *save_file = g_menu_item_new( "Save to file", "app.save_to_file" ); + g_menu_append_item(settings_menu, save_file); + + GMenuItem *quit_app = g_menu_item_new ( "Quit", "app.quit" ); + g_menu_append_item(settings_menu, quit_app); + + // define short-cuts that we can associate with the menu items + // + O for opening a file, + S for saving a file + // + Q for exiting the application + const char *load_accels[2] = { "O", NULL }; + const char *save_accels[2] = { "S", NULL }; + const char *quit_accels[2] = { "Q", NULL }; + + // set up the app actions (callbacks) that are taken when an item + // in the menu is pressed + static GActionEntry app_entries[] = + { + { "load_from_file", load_from_file_activated, NULL, NULL, NULL }, + { "save_to_file", save_to_file_activated, NULL, NULL, NULL }, + { "quit", quit_activated, NULL, NULL, NULL } + }; + + g_action_map_add_action_entries(G_ACTION_MAP (app), app_entries, G_N_ELEMENTS(app_entries), app); + + // apply the short-cuts that are defined above + gtk_application_set_accels_for_action(GTK_APPLICATION (app), "app.load_from_file", load_accels); + gtk_application_set_accels_for_action(GTK_APPLICATION (app), "app.save_to_file", save_accels); + gtk_application_set_accels_for_action(GTK_APPLICATION (app), "app.quit", quit_accels); + + gtk_menu_button_set_menu_model((GtkMenuButton*) menubutton, G_MENU_MODEL(settings_menu)); + + // create headerbar to contain the menu button + GtkWidget *headerbar = gtk_header_bar_new(); + gtk_header_bar_pack_end((GtkHeaderBar*) headerbar, menubutton); + + // set the title of the headerbar + const char *window_title_label = "DS:PoF Attack and Damage Roll Calculator"; + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), window_title_label); + gtk_header_bar_set_title_widget((GtkHeaderBar*) headerbar, label); + + // pass the headerbar to the window + gtk_window_set_titlebar(GTK_WINDOW (window), headerbar); + + // --------------------------------------------------------- + // Open the application window + // --------------------------------------------------------- + + gtk_window_present (GTK_WINDOW (window)); +} + +int main (int argc, char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gtk.example", 0); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} + + + + + + + + +// static void activate ( GApplication *app, G_GNUC_UNUSED gpointer *data ) +// { +// GtkWidget *win; +// GSimpleAction *act_connect; +// GSimpleAction *act_disconnect; + +// /// *** +// GMenu *menu_bar; +// GMenu *network_menu; +// GMenu *server_menu; + +// /// *** +// GMenuItem *menu_item_connect; +// GMenuItem *menu_item_disconnect; + +// /// *** Menu Bar +// menu_bar = g_menu_new(); + +// /// *** Network_Menu +// network_menu = g_menu_new(); +// g_menu_append_submenu ( menu_bar, "Network", G_MENU_MODEL ( network_menu ) ); + +// /// *** Server_Menu +// server_menu = g_menu_new(); +// g_menu_append_submenu ( network_menu, "Server", G_MENU_MODEL ( server_menu ) ); +// /// *** + +// /// *** Create Connect and Disconnect Actions +// act_connect = g_simple_action_new ( "connect", NULL ); +// act_disconnect = g_simple_action_new ( "disconnect", NULL ); + +// /// *** Add them to the ActionMap +// g_action_map_add_action ( G_ACTION_MAP ( app ), G_ACTION ( act_connect ) ); +// g_action_map_add_action ( G_ACTION_MAP ( app ), G_ACTION ( act_disconnect ) ); + +// /// *** Connect them to the activate Signal +// g_signal_connect ( act_connect, "activate", G_CALLBACK ( action_clbk ), NULL ); +// g_signal_connect ( act_disconnect, "activate", G_CALLBACK ( action_clbk ), NULL ); + +// /// *** Create the Connect Item +// menu_item_connect = g_menu_item_new ( "Connect", "app.connect" ); +// g_menu_append_item ( server_menu, menu_item_connect ); + +// /// *** Create the Disconnect Item +// menu_item_disconnect = g_menu_item_new ( "Disconnect", "app.disconnect" ); +// g_menu_append_item ( server_menu, menu_item_disconnect ); + +// /// *** +// gtk_application_set_menubar ( GTK_APPLICATION ( app ), G_MENU_MODEL ( menu_bar ) ); +// gtk_application_window_set_show_menubar ( GTK_APPLICATION_WINDOW ( win ), TRUE ); + +// /// *** +// gtk_window_present ( GTK_WINDOW ( win ) ); + +// /// *** Clean +// g_object_unref ( act_connect ); +// g_object_unref ( act_disconnect ); +// g_object_unref ( menu_item_connect ); +// g_object_unref ( menu_item_disconnect ); +// g_object_unref ( server_menu ); +// g_object_unref ( network_menu ); +// g_object_unref ( menu_bar ); +// } \ No newline at end of file