WindowMaker dockapp emulating a digital pet
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
16 KiB

/*
wmgotchi - a dockapp pet thing
Copyright (C) 2015 - Marco "eukara" Hladik
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 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
E-Mail: eukos@oldtimes-software.com
*/
#include <gtk/gtk.h> // graphics
#include <gdk/gdkx.h> // ditto
#include <unistd.h> // file access
#include <sys/types.h> // nix stuff
#include <pwd.h> // pwd for home dir
// graphics
#include "back.xpm"
#include "icons/title.xpm"
#include "icons/food.xpm"
#include "icons/loo.xpm"
#include "icons/fun.xpm"
#include "icons/hygiene.xpm"
#include "icons/social.xpm"
#include "icons/health.xpm"
#include "icons/wake.xpm"
#include "icons/info.xpm"
// different screens
#define GOTCHI_IDLE 0
#define GOTCHI_FOOD 1
#define GOTCHI_LOO 2
#define GOTCHI_FUN 3
#define GOTCHI_HYGIENE 4
#define GOTCHI_SOCIAL 5
#define GOTCHI_HEALTH 6
#define GOTCHI_WAKE 7
//#define GOTCHI_INFO 8
#define GOTCHI_DEAD 9
#define GOTCHI_SKIN "default"
#define GOTCHI_MAXVAL 10000
// attributes
static int gotchi_food;
static int gotchi_loo;
static int gotchi_fun;
static int gotchi_hygiene;
static int gotchi_social;
static int gotchi_health;
static int gotchi_wake;
static int gotchi_age;
// global stuff
static gint game_clock = 0;
static int game_seconds = 0;
static int game_nextthink = 0;
static int gotchi_mode = 0;
static int gotchi_frame = 0;
char gotchi_config[80];
GtkImage *gotch_face;
void gotchi_display_info() {
GtkWidget *window;
GtkWidget *table;
GtkWidget *lblfooda;
gchar *values[16] = {
"Food", g_strdup_printf("%i%%", (gotchi_food / 100)),
"Loo", g_strdup_printf("%i%%", (gotchi_loo / 100)),
"Fun", g_strdup_printf("%i%%", (gotchi_fun / 100)),
"Hygiene", g_strdup_printf("%i%%", (gotchi_hygiene / 100)),
"Social", g_strdup_printf("%i%%", (gotchi_social / 100)),
"Health", g_strdup_printf("%i%%", (gotchi_health / 100)),
"Awake", g_strdup_printf("%i%%", (gotchi_wake / 100)),
"Minutes passed", g_strdup_printf("%im", (game_seconds / 60))
};
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "wmgotchi info");
gtk_window_set_default_size(GTK_WINDOW(window), 190, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
table = gtk_table_new(8, 2, TRUE);
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
int i = 0;
int j = 0;
int pos = 0;
for (i=0; i < 8; i++) {
for (j=0; j < 2; j++) {
lblfooda = gtk_label_new(values[pos]);
gtk_table_attach_defaults(GTK_TABLE(table), lblfooda, j, j+1, i, i+1);
pos++;
}
}
gtk_container_add(GTK_CONTAINER(window), table);
gtk_widget_show_all(window);
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
}
// this makes this GTK app act like a dockapp
GtkWidget *make_dockapp(GtkWidget *mw){
GdkDisplay *display;
GtkWidget *foobox;
Display *d;
Window mainwin, iw, w, p, dummy1, *dummy2;
unsigned int dummy3;
XWMHints *wmHints;
display = gdk_display_get_default();
foobox = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_wmclass(GTK_WINDOW(mw), g_get_prgname(), "DockApp");
gtk_widget_set_size_request(foobox, 59, 58);
gtk_widget_realize(mw);
gtk_widget_realize(foobox);
d = GDK_DISPLAY_XDISPLAY(display);
mainwin = GDK_WINDOW_XID(gtk_widget_get_window(mw));
iw = GDK_WINDOW_XID(gtk_widget_get_window(foobox));
XQueryTree(d, mainwin, &dummy1, &p, &dummy2, &dummy3);
if (dummy2)
XFree(dummy2);
w = XCreateSimpleWindow(d, p, 0, 0, 1, 1, 0, 0, 0);
XReparentWindow(d, mainwin, w, 0, 0);
gtk_widget_show(mw);
gtk_widget_show(foobox);
wmHints = XGetWMHints(d, mainwin);
if (!wmHints)
wmHints = XAllocWMHints();
wmHints->flags |= IconWindowHint;
wmHints->icon_window = iw;
XSetWMHints(d, mainwin, wmHints);
XFree(wmHints);
XReparentWindow(d, mainwin, p, 0, 0);
XDestroyWindow(d, w);
return foobox;
}
int gotchi_modifystats(int attribute, int value){
attribute += value;
if(attribute > GOTCHI_MAXVAL)
attribute = GOTCHI_MAXVAL;
else if(attribute < 0)
attribute = 0;
return attribute;
}
// input functions
void gotchi_food_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_FOOD;
gotchi_food = gotchi_modifystats(gotchi_food, 5000);
gotchi_loo = gotchi_modifystats(gotchi_loo, -1500);
gotchi_hygiene = gotchi_modifystats(gotchi_hygiene, -500);
printf("Food!\n");}
void gotchi_loo_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_LOO;
gotchi_loo = gotchi_modifystats(gotchi_loo, 10000);
gotchi_hygiene = gotchi_modifystats(gotchi_hygiene, -800);
gotchi_fun = gotchi_modifystats(gotchi_fun, 100);
printf("Toilet!\n");}
void gotchi_fun_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_FUN;
gotchi_fun = gotchi_modifystats(gotchi_fun, 3000);
gotchi_food = gotchi_modifystats(gotchi_food, -450);
gotchi_hygiene = gotchi_modifystats(gotchi_hygiene, -300);
gotchi_wake = gotchi_modifystats(gotchi_wake, -400);
printf("Sports and Fun!\n");}
void gotchi_hygiene_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_HYGIENE;
gotchi_hygiene = gotchi_modifystats(gotchi_hygiene, 8000);
gotchi_fun = gotchi_modifystats(gotchi_fun, 100);
printf("Shower!\n");}
void gotchi_social_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_SOCIAL;
gotchi_fun = gotchi_modifystats(gotchi_fun, 1000);
gotchi_social = gotchi_modifystats(gotchi_social, 3500);
gotchi_food = gotchi_modifystats(gotchi_food, -350);
printf("Talking!\n");}
void gotchi_health_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds || gotchi_mode != GOTCHI_IDLE)
return;
game_nextthink = game_seconds + 5;
gotchi_mode = GOTCHI_HEALTH;
gotchi_health = gotchi_modifystats(gotchi_health, 4000);
gotchi_fun = gotchi_modifystats(gotchi_fun, -1000);
printf("Aid!\n");}
void gotchi_wake_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(game_nextthink > game_seconds)
return;
if(gotchi_mode == GOTCHI_IDLE)
gotchi_mode = GOTCHI_WAKE;
else if(gotchi_mode == GOTCHI_WAKE)
gotchi_mode = GOTCHI_IDLE;
else
return;
printf("Sleep!\n");}
void gotchi_info_pressed(GtkWidget *widget, GdkEvent *event){
if(gotchi_age == 0)
return;
if(gotchi_mode == GOTCHI_IDLE || gotchi_mode == GOTCHI_WAKE)
gotchi_display_info();
else
return;
printf("Info!\n");}
// change the pix of the gotchi
void gotchi_animate(int age, char *frame){
char filename[80];
sprintf(filename, "./chars/%s/%d%s%d.xpm", GOTCHI_SKIN, age,frame,gotchi_frame);
gotchi_frame = 1 - gotchi_frame;
gtk_image_set_from_file(gotch_face, filename);
printf("animating gotchi: %s\n", filename);
}
// age the gotchi
void gotchi_level(){
gotchi_age++;
if(gotchi_age == 5)
gotchi_mode = GOTCHI_DEAD;
printf("your wmgotchi aged! it's now %d years old\n", gotchi_age);
}
// main loop for the frameupdate (1 sec)
gint gotchi_update (gpointer data)
{
if(gotchi_age < 5)
game_seconds++;
if(game_seconds % 30 == 0)
gotchi_save_character();
// hatch after 60s
if(game_seconds == 6 || game_seconds == 7200 || game_seconds == 14400 || game_seconds == 21600 || game_seconds == 28800){
gotchi_level();}
if(gotchi_mode == GOTCHI_WAKE && gotchi_wake > GOTCHI_MAXVAL - 50) // HACK! Go idle when fully rested
gotchi_mode == GOTCHI_IDLE;
if(game_seconds == game_nextthink)
gotchi_mode = GOTCHI_IDLE;
// animate it
switch(gotchi_mode){
case GOTCHI_IDLE:
gotchi_animate(gotchi_age, "idle");
break;
case GOTCHI_FOOD:
gotchi_animate(gotchi_age, "food");
break;
case GOTCHI_LOO:
gotchi_animate(gotchi_age, "loo");
break;
case GOTCHI_FUN:
gotchi_animate(gotchi_age, "fun");
break;
case GOTCHI_HYGIENE:
gotchi_animate(gotchi_age, "shower");
break;
case GOTCHI_SOCIAL:
gotchi_animate(gotchi_age, "social");
break;
case GOTCHI_HEALTH:
gotchi_animate(gotchi_age, "health");
break;
case GOTCHI_WAKE:
gotchi_animate(gotchi_age, "sleep");
break;
/*case GOTCHI_INFO:
printf("INFO! FD: %d, LO: %d, FN: %d, HG: %d, SC: %d, HP: %d, WK: %d\n",gotchi_food, gotchi_loo, gotchi_fun, gotchi_hygiene, gotchi_social, gotchi_health, gotchi_wake);
break;*/
case GOTCHI_DEAD:
gotchi_animate(0, "dead");
break;
}
// make the gotchi experience
if(gotchi_mode == GOTCHI_WAKE)
{
gotchi_wake = gotchi_modifystats(gotchi_wake, 2);
gotchi_food = gotchi_modifystats(gotchi_food, -1);
gotchi_social = gotchi_modifystats(gotchi_social, -1);
}
else
{
gotchi_food = gotchi_modifystats(gotchi_food, -1);
gotchi_wake = gotchi_modifystats(gotchi_wake, -1);
gotchi_loo = gotchi_modifystats(gotchi_loo, -1);
gotchi_hygiene = gotchi_modifystats(gotchi_hygiene, -1);
gotchi_fun = gotchi_modifystats(gotchi_fun, -1);
gotchi_social = gotchi_modifystats(gotchi_social, -2);
}
if((gotchi_food+gotchi_fun+gotchi_loo+gotchi_hygiene+gotchi_social+gotchi_wake) < (GOTCHI_MAXVAL * 3))
gotchi_health = gotchi_modifystats(gotchi_health, -100);
if(gotchi_health <= 0)
gotchi_mode = GOTCHI_DEAD;
return 1;
}
void gotchi_save_character(){
FILE *fFile;
fFile = fopen(gotchi_config, "w+");
fprintf(fFile, "%d\n", gotchi_food);
fprintf(fFile, "%d\n", gotchi_loo);
fprintf(fFile, "%d\n", gotchi_fun);
fprintf(fFile, "%d\n", gotchi_hygiene);
fprintf(fFile, "%d\n", gotchi_social);
fprintf(fFile, "%d\n", gotchi_health);
fprintf(fFile, "%d\n", gotchi_wake);
fprintf(fFile, "%d\n", game_clock);
fprintf(fFile, "%d\n", game_seconds);
fprintf(fFile, "%d\n", gotchi_age);
fclose(fFile);
printf("saved state...\n");
}
// load savestate or create a new gotchi
void gotchi_init_character(){
FILE *fFile;
uid_t uid = getuid();
struct passwd *pw = getpwuid(uid);
sprintf(gotchi_config, "%s/.wmgotchirc", pw->pw_dir);
if(access(gotchi_config, R_OK) != -1 ) {
fFile = fopen(gotchi_config, "r");
fscanf(fFile,"%d %d %d %d %d %d %d %d %d %d", &gotchi_food, &gotchi_loo, &gotchi_fun, &gotchi_hygiene, &gotchi_social, &gotchi_health, &gotchi_wake, &game_clock, &game_seconds, &gotchi_age);
fclose(fFile);
} else {
gotchi_food = gotchi_loo = gotchi_fun = gotchi_hygiene = gotchi_social = gotchi_health = gotchi_wake = GOTCHI_MAXVAL;
game_clock = game_seconds = gotchi_age = 0;
gotchi_save_character();
}
}
// initialize everything
int main(int argc, char *argv[]){
GtkWidget *gtkiw;
GtkWidget *dockArea;
GtkWidget *fixed;
GtkWidget *background;
GtkStyle *style;
GdkBitmap *mask10, *mask32, *mask58;
GdkPixmap *titlepix, *backpix, *foodpix, *loopix, *funpix, *hygienepix, *socialpix, *healthpix, *wakepix, *infopix;
GtkWidget *food, *loo, *fun, *hygiene, *social, *health, *wake, *info;
gtk_init(&argc, &argv);
gotchi_init_character();
fixed = gtk_fixed_new();
gtkiw = gtk_window_new(GTK_WINDOW_TOPLEVEL);
dockArea = make_dockapp(gtkiw);
gtk_container_add(GTK_CONTAINER(dockArea), fixed);
style = gtk_widget_get_style(dockArea);
titlepix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask32, NULL, (gchar **)title_xpm );
backpix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask58, NULL, (gchar **)back_xpm );
foodpix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)food_xpm );
loopix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)loo_xpm );
funpix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)fun_xpm );
hygienepix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)hygiene_xpm );
socialpix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)social_xpm );
healthpix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)health_xpm );
wakepix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)wake_xpm );
infopix = gdk_pixmap_create_from_xpm_d( dockArea->window, &mask10, NULL, (gchar **)info_xpm );
food = gtk_event_box_new();
loo = gtk_event_box_new();
fun = gtk_event_box_new();
hygiene = gtk_event_box_new();
social = gtk_event_box_new();
health = gtk_event_box_new();
wake = gtk_event_box_new();
info = gtk_event_box_new();
gotch_face = gtk_image_new_from_pixmap(titlepix, mask32);
background = gtk_image_new_from_pixmap(backpix, mask58);
gtk_fixed_put(GTK_FIXED(fixed), background, 0, 0);
gtk_fixed_put(GTK_FIXED(fixed), food, 5, 1);
gtk_fixed_put(GTK_FIXED(fixed), loo, 18, 1);
gtk_fixed_put(GTK_FIXED(fixed), fun, 31, 1);
gtk_fixed_put(GTK_FIXED(fixed), hygiene, 44, 1);
gtk_fixed_put(GTK_FIXED(fixed), social, 5, 47);
gtk_fixed_put(GTK_FIXED(fixed), health, 18, 47);
gtk_fixed_put(GTK_FIXED(fixed), wake, 31, 47);
gtk_fixed_put(GTK_FIXED(fixed), info, 44, 47);
gtk_fixed_put(GTK_FIXED(fixed), gotch_face, 14, 13);
gtk_widget_set_size_request (food, 10, 10);
gtk_widget_set_size_request (loo, 10, 10);
gtk_widget_set_size_request (fun, 10, 10);
gtk_widget_set_size_request (hygiene, 10, 10);
gtk_widget_set_size_request (social, 10, 10);
gtk_widget_set_size_request (health, 10, 10);
gtk_widget_set_size_request (wake, 10, 10);
gtk_widget_set_size_request (info, 10, 10);
gtk_widget_set_size_request (gotch_face, 32, 32);
GtkWidget *foodimg = gtk_image_new_from_pixmap (foodpix, mask10);
gtk_container_add(GTK_CONTAINER(food), foodimg);
GtkWidget *looimg = gtk_image_new_from_pixmap (loopix, mask10);
gtk_container_add(GTK_CONTAINER(loo), looimg);
GtkWidget *funimg = gtk_image_new_from_pixmap (funpix, mask10);
gtk_container_add(GTK_CONTAINER(fun), funimg);
GtkWidget *hygieneimg = gtk_image_new_from_pixmap (hygienepix, mask10);
gtk_container_add(GTK_CONTAINER(hygiene), hygieneimg);
GtkWidget *socialimg = gtk_image_new_from_pixmap (socialpix, mask10);
gtk_container_add(GTK_CONTAINER(social), socialimg);
GtkWidget *healthimg = gtk_image_new_from_pixmap (healthpix, mask10);
gtk_container_add(GTK_CONTAINER(health), healthimg);
GtkWidget *wakeimg = gtk_image_new_from_pixmap (wakepix, mask10);
gtk_container_add(GTK_CONTAINER(wake), wakeimg);
GtkWidget *infoimg = gtk_image_new_from_pixmap (infopix, mask10);
gtk_container_add(GTK_CONTAINER(info), infoimg);
gtk_widget_show_all(dockArea);
g_signal_connect(food, "button-press-event", G_CALLBACK(gotchi_food_pressed), NULL);
g_signal_connect(loo, "button-press-event", G_CALLBACK(gotchi_loo_pressed), NULL);
g_signal_connect(fun, "button-press-event", G_CALLBACK(gotchi_fun_pressed), NULL);
g_signal_connect(hygiene, "button-press-event", G_CALLBACK(gotchi_hygiene_pressed), NULL);
g_signal_connect(social, "button-press-event", G_CALLBACK(gotchi_social_pressed), NULL);
g_signal_connect(health, "button-press-event", G_CALLBACK(gotchi_health_pressed), NULL);
g_signal_connect(wake, "button-press-event", G_CALLBACK(gotchi_wake_pressed), NULL);
g_signal_connect(info, "button-press-event", G_CALLBACK(gotchi_info_pressed), NULL);
game_clock = gtk_timeout_add (1000, gotchi_update, NULL);
gtk_main();
return 0;
}