mirror of
https://git.squeakyrat.moe/petratc/
synced 2026-11-04 18:35:43 +01:00
714 lines
23 KiB
C
714 lines
23 KiB
C
#include "../rat.h"
|
|
|
|
/*
|
|
i lowkey stole the art of the little rat and the cheese from the internet lol
|
|
https://asciiart.website/art/752
|
|
*/
|
|
|
|
static int running;
|
|
|
|
|
|
static void stop() {
|
|
clear();
|
|
refresh();
|
|
endwin();
|
|
running = 0;
|
|
}
|
|
|
|
static char *cheeseart = {
|
|
" ()() ____\n (..) /|o |\n /\\/\\ /o| o|\nc\\db/o... /o_|_o_|"
|
|
};
|
|
//i hate this
|
|
|
|
/*
|
|
//i need to make a system based on intergers to represent non-int numbers like 0.5 or five trillion as 5T instead of 500000000000 or whatever
|
|
//how do i tell a computer that 1000 = 1k and 10000 = 10k and 1000000 = 1M and that etc
|
|
//i'll use powers of 10
|
|
//1^10x0 = 1 1^1(10x1) = 10 1^1(10x2) = 100 1^1(10x3) = 1k
|
|
// x*10^y <-- this is what i store
|
|
//1 becomes X. x can range from 0 to 999, when it reaches 999 it adds 1 to y and x goes back to 0
|
|
//is this memory safe????? yeah sure i don't see why it wouldnt
|
|
//x is a float or a double i think a double yeah a double
|
|
//
|
|
//and then print that as it should so 2.3^10x3 = 2.3k
|
|
//and also other functions to add or substract from this using regular numbers
|
|
|
|
//wait i could just do powers of 1000
|
|
//x is still 0.0 - 999.999 but it's x*1000^y
|
|
//55.3* 1000^2 = 55300000
|
|
//
|
|
//once x reaches 999.999 it wraps back to 0 and adds 1 to y
|
|
//then check y so that y = 0 is units, y = 1 is k, y = 2 is M, y = 3 is B, y = 4 is T, etc
|
|
*/
|
|
|
|
/*
|
|
//a bignum is just a scientific notation number
|
|
// x ^ 1000^y
|
|
// 55300 = 55.3*1000^1
|
|
// double x = 55.3
|
|
// int y = 1
|
|
*/
|
|
typedef struct bignum{
|
|
double x;
|
|
int y;
|
|
}bignum;
|
|
|
|
typedef struct ratworker{
|
|
char *name;
|
|
bignum price;
|
|
////line 67 six seveeennn
|
|
bignum clicks;
|
|
bignum multiplier; //the more you buy the more expensive the unit becomes
|
|
bignum owned;
|
|
}ratworker;
|
|
|
|
typedef struct{
|
|
char *name;
|
|
bignum price;
|
|
bignum multiplier;
|
|
char *whichstat;
|
|
int howmanystat; //how many times itc alls addstat() for that stat
|
|
}prizes;
|
|
|
|
#define nhowmanytiers 11
|
|
#define nhowmanyprizes 10
|
|
|
|
static char *digit[] = {
|
|
"", "K", "M", "B", "T",
|
|
"Qa", "Qi", "Sx", "Sp",
|
|
"Oc", "No", "Dc"
|
|
// up to y=11 (10^36) there's no way someone gets farther than this
|
|
};
|
|
|
|
static bignum normalize(double regnum){
|
|
bignum b;
|
|
double val = regnum;
|
|
int y = 0;
|
|
|
|
while (fabs(val) >= 1000) {
|
|
val /= 1000.0;
|
|
y++;
|
|
}
|
|
while (val != 0.0 && fabs(val) < 1) {
|
|
val *= 1000.0;
|
|
y--;
|
|
}
|
|
|
|
b.x = val;
|
|
b.y = y;
|
|
return b;
|
|
}
|
|
|
|
static bignum normalizebignum(bignum b){
|
|
double val = b.x;
|
|
int y = b.y;
|
|
|
|
while (fabs(val) >= 1000) {
|
|
val /= 1000.0;
|
|
y++;
|
|
}
|
|
while (val != 0.0 && fabs(val) < 1) {
|
|
val *= 1000.0;
|
|
y--;
|
|
}
|
|
|
|
b.x = val;
|
|
b.y = y;
|
|
return b;
|
|
}
|
|
|
|
static bignum addbignum(bignum a, bignum b){
|
|
bignum result;
|
|
if(a.y == b.y){
|
|
result.x = a.x + b.x;
|
|
result.y = a.y;
|
|
} else if(a.y > b.y){
|
|
double bx = b.x;
|
|
for(int i = a.y - b.y; i > 0; i--) bx /= 1000.0;
|
|
result.x = a.x + bx;
|
|
result.y = a.y;
|
|
} else {
|
|
double ax = a.x;
|
|
for(int i = b.y - a.y; i > 0; i--) ax /= 1000.0;
|
|
result.x = ax + b.x;
|
|
result.y = b.y;
|
|
}
|
|
return normalizebignum(result);
|
|
}
|
|
|
|
static bignum subbignum(bignum a, bignum b){
|
|
bignum result;
|
|
if(a.y == b.y){
|
|
result.x = a.x - b.x;
|
|
result.y = a.y;
|
|
} else if(a.y > b.y){
|
|
double bx = b.x;
|
|
for(int i = a.y - b.y; i > 0; i--) bx /= 1000.0;
|
|
result.x = a.x - bx;
|
|
result.y = a.y;
|
|
} else {
|
|
double ax = a.x;
|
|
for(int i = b.y - a.y; i > 0; i--) ax /= 1000.0;
|
|
result.x = ax - b.x;
|
|
result.y = b.y;
|
|
}
|
|
return normalizebignum(result);
|
|
}
|
|
|
|
static bignum mulbignum(bignum a, bignum b){
|
|
bignum result;
|
|
result.x = a.x * b.x;
|
|
result.y = a.y + b.y;
|
|
return normalizebignum(result);
|
|
}
|
|
|
|
static bignum divbignum(bignum a, bignum b){
|
|
bignum result;
|
|
result.x = a.x / b.x;
|
|
result.y = a.y - b.y;
|
|
return normalizebignum(result);
|
|
}
|
|
|
|
/*
|
|
* returns 1 if a is larger
|
|
* returns 0 if same number
|
|
* returns -1 is a is tiny
|
|
*/
|
|
static int comparebignum(bignum a, bignum b){
|
|
//branch predictors will hate this function :sob:
|
|
if(a.y == b.y){
|
|
if(a.x == b.x){
|
|
return 0;
|
|
}else if(a.x > b.x){
|
|
return 1;
|
|
}else return -1;
|
|
}else if(a.y > b.y){
|
|
return 1;
|
|
} else return -1;
|
|
}
|
|
|
|
static bignum addmixregbig(bignum a, double regnum){
|
|
return addbignum(a, normalize(regnum));
|
|
}
|
|
static bignum submixregbig(bignum a, double regnum){
|
|
return subbignum(a, normalize(regnum));
|
|
}
|
|
static bignum mulmixregbig(bignum a, double regnum){
|
|
return mulbignum(a, normalize(regnum));
|
|
}
|
|
static bignum divmixregbig(bignum a, double regnum){
|
|
return divbignum(a, normalize(regnum));
|
|
}
|
|
|
|
/* clamps a bignum to zero if it somehow goes negative */
|
|
static bignum clampnonneg(bignum b){
|
|
if(b.x < 0.0 || b.y < 0) return (bignum){0.0, 0};
|
|
return b;
|
|
}
|
|
|
|
static bignum calculateworkerincome(ratworker worker){
|
|
return mulbignum(worker.clicks, worker.owned);
|
|
}
|
|
|
|
static bignum calculatetotalincome(ratworker workers[], int n){
|
|
bignum total = {0.0, 0};
|
|
for(int i = 0; i < n; i++){
|
|
total = addbignum(total, calculateworkerincome(workers[i]));
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static int buyworker(bignum *cheeses, ratworker *worker){
|
|
if(comparebignum(*cheeses, worker->price) < 0) return 0; //nigga broke
|
|
|
|
*cheeses = subbignum(*cheeses, worker->price);
|
|
*cheeses = clampnonneg(*cheeses);
|
|
worker->owned = addmixregbig(worker->owned, 1.0);
|
|
worker->price = mulbignum(worker->price, worker->multiplier);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int buyprize(bignum *cheeses, prizes *prize){
|
|
if(comparebignum(*cheeses, prize->price) < 0) return 0;
|
|
|
|
*cheeses = subbignum(*cheeses, prize->price);
|
|
*cheeses = clampnonneg(*cheeses);
|
|
|
|
prize->price = mulbignum(prize->price, prize->multiplier);
|
|
|
|
float *stat = NULL;
|
|
if(strcmp(prize->whichstat, "hunger") == 0) stat = &rat.hunger;
|
|
else if(strcmp(prize->whichstat, "health") == 0) stat = &rat.health;
|
|
else if(strcmp(prize->whichstat, "love") == 0) stat = &rat.love;
|
|
else if(strcmp(prize->whichstat, "fun") == 0) stat = &rat.fun;
|
|
else if(strcmp(prize->whichstat, "clean") == 0) stat = &rat.clean;
|
|
|
|
if(stat){
|
|
for(int i = 0; i < prize->howmanystat; i++){
|
|
addstat(stat);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static char *bignumprint(bignum b) {
|
|
//32 bytes is more than enough for 124.24 trillion idk
|
|
char *nice = malloc(32 * sizeof(char));
|
|
if(nice == NULL) return NULL;
|
|
|
|
if(b.y >= 0 && b.y < 12) {
|
|
snprintf(nice, 32, "%.2lf%s", b.x, digit[b.y]);
|
|
} else{
|
|
snprintf(nice, 32, "%.2lfe3*%d", b.x, b.y);
|
|
}
|
|
return nice;
|
|
}
|
|
|
|
|
|
|
|
//wide = art and score side by side
|
|
//narrow = art on topand score below
|
|
typedef enum { layoutnarrow, layoutwide } layoutmode;
|
|
|
|
//bounding box of the drawn cheese art in winlocal coordinates for click detection
|
|
static int artrow0, artcol0, artrows, artcols;
|
|
|
|
static layoutmode getlayout(WINDOW *win){
|
|
return (getmaxx(win) >= getmaxy(win) * 2) ? layoutwide : layoutnarrow;
|
|
}
|
|
|
|
static void drawwin(WINDOW *win, bignum cheeses, ratworker workers[], int n){
|
|
werase(win);
|
|
box(win, 0, 0);
|
|
|
|
int winw = getmaxx(win);
|
|
layoutmode mode = getlayout(win);
|
|
|
|
char artcopy[2048];
|
|
strncpy(artcopy, cheeseart, sizeof(artcopy));
|
|
artcopy[sizeof(artcopy)-1] = '\0';
|
|
|
|
int artcol = 2;
|
|
int artrow = 1;
|
|
int scorecol, scorewrapw;
|
|
|
|
if(mode == layoutwide){
|
|
scorecol = winw / 2 - 25;
|
|
scorewrapw = winw - scorecol - 2;
|
|
} else {
|
|
scorecol = 2;
|
|
scorewrapw = winw - 4;
|
|
}
|
|
|
|
int row = artrow;
|
|
int widest = 0;
|
|
char *line = strtok(artcopy, "\n");
|
|
while(line){
|
|
mvwprintw(win, row, artcol, "%s", line);
|
|
int len = strlen(line);
|
|
if(len > widest) widest = len;
|
|
row++;
|
|
line = strtok(NULL, "\n");
|
|
}
|
|
|
|
//save where the art landed so the click handler knows what to hit-test against
|
|
artrow0 = artrow;
|
|
artcol0 = artcol;
|
|
artrows = row - artrow;
|
|
artcols = widest;
|
|
|
|
char *scorestr = bignumprint(cheeses);
|
|
char scoremsg[64];
|
|
if(scorestr != NULL) {
|
|
snprintf(scoremsg, sizeof(scoremsg), "cheese: %s", scorestr);
|
|
free(scorestr);
|
|
} else {
|
|
snprintf(scoremsg, sizeof(scoremsg), "cheese: ERROR");
|
|
}
|
|
char *incomestr = bignumprint(calculatetotalincome(workers, n));
|
|
char incomemsg[64];
|
|
if(incomestr != NULL) {
|
|
snprintf(incomemsg, sizeof(incomemsg), "income/s: %s", incomestr);
|
|
free(incomestr);
|
|
} else {
|
|
snprintf(incomemsg, sizeof(incomemsg), "income/s: ERROR");
|
|
}
|
|
|
|
if(mode == layoutwide){
|
|
mvwprintw(win, 1, scorecol, "%s", scoremsg);
|
|
mvwprintw(win, 2, scorecol, "%s", incomemsg);
|
|
wrapprint(win, 3, scorecol, scorewrapw, "click the cheese!");;
|
|
} else {
|
|
int scorerow = artrow0 + artrows + 1;
|
|
wrapprint(win, scorerow, scorecol, scorewrapw, scoremsg);
|
|
wrapprint(win, scorerow+1, scorecol, scorewrapw, incomemsg);
|
|
wrapprint(win, scorerow+2, scorecol, scorewrapw, "click the cheese!");
|
|
}
|
|
}
|
|
|
|
#define shopwidth 45
|
|
static void drawshop(WINDOW *win, ratworker workers[], int n, int selected, bignum cheeses, int active){
|
|
int winh = getmaxy(win);
|
|
int winw = getmaxx(win);
|
|
|
|
int shoph = n + 2;
|
|
if(shoph > winh / 2) shoph = winh / 2;
|
|
if(shoph > winh - 2) shoph = winh - 2;
|
|
int shopw = shopwidth;
|
|
if(shopw > winw - 4) shopw = winw - 4;
|
|
if(shoph < 3 || shopw < 10) return;
|
|
//if that skip it
|
|
|
|
int shopy;
|
|
if(getlayout(win) == layoutwide){
|
|
shopy = 1;
|
|
}else{
|
|
int pshoph = (nhowmanyprizes + 2);
|
|
if(pshoph > winh / 2) pshoph = winh / 2;
|
|
shopy = winh - shoph - pshoph - 1;
|
|
if(shopy < 1) shopy = 1;
|
|
}
|
|
|
|
int shopx = winw - shopw - 2;
|
|
|
|
mvwhline(win, shopy, shopx + 1, 0, shopw - 2);
|
|
mvwhline(win, shopy + shoph - 1, shopx + 1, 0, shopw - 2);
|
|
mvwvline(win, shopy + 1, shopx, 0, shoph - 2);
|
|
mvwvline(win, shopy + 1, shopx + shopw - 1, 0, shoph - 2);
|
|
mvwaddch(win, shopy, shopx, ACS_ULCORNER);
|
|
mvwaddch(win, shopy, shopx + shopw - 1, ACS_URCORNER);
|
|
mvwaddch(win, shopy + shoph - 1, shopx, ACS_LLCORNER);
|
|
mvwaddch(win, shopy + shoph - 1, shopx + shopw - 1, ACS_LRCORNER);
|
|
|
|
if(active) wattron(win, A_BOLD | A_REVERSE);
|
|
mvwprintw(win, shopy, shopx + 2, " workers (tab,up/down,enter) ");
|
|
if(active) wattroff(win, A_BOLD | A_REVERSE);
|
|
|
|
int availw = shopw - 2;
|
|
int visible = shoph - 2;
|
|
int start = 0;
|
|
if(selected >= visible) start = selected - visible + 1;
|
|
if(start > n - visible) start = n - visible;
|
|
if(start < 0) start = 0;
|
|
|
|
for(int i = start; i < start + visible && i < n; i++){
|
|
ratworker *w = &workers[i];
|
|
char *pricestr = bignumprint(w->price);
|
|
char *ownedstr = bignumprint(w->owned);
|
|
char rightstr[32];
|
|
snprintf(rightstr, sizeof(rightstr), "$%s x%s",
|
|
pricestr ? pricestr : "?", ownedstr ? ownedstr : "?");
|
|
if(pricestr) free(pricestr);
|
|
if(ownedstr) free(ownedstr);
|
|
|
|
int rightlen = strlen(rightstr);
|
|
int namewidth = availw - rightlen - 1; //-1 for a gap column
|
|
if(namewidth < 0) namewidth = 0;
|
|
|
|
char row[64];
|
|
//fill the whole width first so shrinking a wider row doesn't leave weird gaps
|
|
snprintf(row, sizeof(row), "%-*.*s", availw, availw, "");
|
|
|
|
int affordable = comparebignum(cheeses, w->price) >= 0;
|
|
if(active && i == selected) wattron(win, A_REVERSE);
|
|
else if(!affordable) wattron(win, A_DIM);
|
|
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1, "%s", row);
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1, "%-*.*s", namewidth, namewidth, w->name);
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1 + availw - rightlen, "%s", rightstr);
|
|
|
|
if(active && i == selected) wattroff(win, A_REVERSE);
|
|
else if(!affordable) wattroff(win, A_DIM);
|
|
}//bwehghg
|
|
}
|
|
|
|
#define pshopwidth 45
|
|
static void drawpshop(WINDOW *win, prizes prize[], int n, int selected, bignum cheeses, int active){
|
|
int winh = getmaxy(win);
|
|
int winw = getmaxx(win);
|
|
|
|
int shoph = n + 2;
|
|
if(shoph > winh / 2) shoph = winh / 2;
|
|
if(shoph > winh - 2) shoph = winh - 2;
|
|
int shopw = pshopwidth;
|
|
if(shopw > winw - 4) shopw = winw - 4;
|
|
if(shoph < 3 || shopw < 10) return;
|
|
//if that skip it
|
|
|
|
int shopx = winw - shopw - 2;
|
|
int shopy;
|
|
if(getlayout(win) == layoutwide){
|
|
int wshoph = nhowmanytiers + 2;
|
|
if(wshoph > winh / 2) wshoph = winh / 2;
|
|
shopy = 1 + wshoph;
|
|
if(shopy + shoph > winh - 1) shopy = winh - shoph - 1;
|
|
} else {
|
|
shopy = winh - shoph - 1;
|
|
if(shopy < 1) shopy = 1;
|
|
}
|
|
|
|
mvwhline(win, shopy, shopx + 1, 0, shopw - 2);
|
|
mvwhline(win, shopy + shoph - 1, shopx + 1, 0, shopw - 2);
|
|
mvwvline(win, shopy + 1, shopx, 0, shoph - 2);
|
|
mvwvline(win, shopy + 1, shopx + shopw - 1, 0, shoph - 2);
|
|
mvwaddch(win, shopy, shopx, ACS_ULCORNER);
|
|
mvwaddch(win, shopy, shopx + shopw - 1, ACS_URCORNER);
|
|
mvwaddch(win, shopy + shoph - 1, shopx, ACS_LLCORNER);
|
|
mvwaddch(win, shopy + shoph - 1, shopx + shopw - 1, ACS_LRCORNER);
|
|
|
|
if(active) wattron(win, A_BOLD | A_REVERSE);
|
|
mvwprintw(win, shopy, shopx + 2, " prizes (tab/up/down/enter) ");
|
|
if(active) wattroff(win, A_BOLD | A_REVERSE);
|
|
|
|
int availw = shopw - 2;
|
|
int visible = shoph - 2;
|
|
int start = 0;
|
|
if(selected >= visible) start = selected - visible + 1;
|
|
if(start > n - visible) start = n - visible;
|
|
if(start < 0) start = 0;
|
|
|
|
for(int i = start; i < start + visible && i < n; i++){
|
|
prizes *p = &prize[i];
|
|
char *pricestr = bignumprint(p->price);
|
|
char rightstr[32];
|
|
snprintf(rightstr, sizeof(rightstr), "$%s",
|
|
pricestr ? pricestr : "?");
|
|
if(pricestr) free(pricestr);
|
|
|
|
int rightlen = strlen(rightstr);
|
|
int namewidth = availw - rightlen - 1; //-1 for a gap column
|
|
if(namewidth < 0) namewidth = 0;
|
|
|
|
char row[64];
|
|
//fill the whole width first so shrinking a wider row doesn't leave weird gaps
|
|
snprintf(row, sizeof(row), "%-*.*s", availw, availw, "");
|
|
|
|
int affordable = comparebignum(cheeses, p->price) >= 0;
|
|
if(active && i == selected) wattron(win, A_REVERSE);
|
|
else if(!affordable) wattron(win, A_DIM);
|
|
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1, "%s", row);
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1, "%-*.*s", namewidth, namewidth, p->name);
|
|
mvwprintw(win, shopy + 1 + (i - start), shopx + 1 + availw - rightlen, "%s", rightstr);
|
|
|
|
if(active && i == selected) wattroff(win, A_REVERSE);
|
|
else if(!affordable) wattroff(win, A_DIM);
|
|
}//bwehghg
|
|
}
|
|
|
|
//i guess i will use this in a game... and i have plans
|
|
//for another game that will also require saving hehe
|
|
#ifdef __APPLE__
|
|
#define clickerpath "%s/Library/Application Support/rat"
|
|
#define clickersavepath "%s/Library/Application Support/rat/clicker.txt"
|
|
#else
|
|
#define clickerpath "%s/.local/share/rat"
|
|
#define clickersavepath "%s/.local/share/rat/clicker.txt"
|
|
#endif
|
|
|
|
|
|
static void clickersave(bignum cheeses, ratworker workers[], int n, prizes prize[], int n2){
|
|
char path[256];
|
|
snprintf(path, sizeof(path), clickerpath, getenv("HOME"));
|
|
mkdir(path, 0755);
|
|
snprintf(path, sizeof(path), clickersavepath, getenv("HOME"));
|
|
FILE *f = fopen(path, "w");
|
|
if(!f) return;
|
|
fprintf(f, "%lf\n%d\n", cheeses.x, cheeses.y);
|
|
for(int i = 0; i < n; i++){
|
|
fprintf(f, "%lf\n%d\n%lf\n%d\n",
|
|
workers[i].price.x, workers[i].price.y,
|
|
workers[i].owned.x, workers[i].owned.y);
|
|
}
|
|
for(int i = 0; i < n2; i++){
|
|
fprintf(f, "%lf\n%d\n", prize[i].price.x, prize[i].price.y);
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void clickerload(bignum *cheeses, ratworker workers[], int n, prizes prize[], int n2){
|
|
char path[256];
|
|
snprintf(path, sizeof(path), clickersavepath, getenv("HOME"));
|
|
|
|
FILE *f = fopen(path, "r");
|
|
if(!f) return;
|
|
|
|
if(fscanf(f, "%lf\n%d\n", &cheeses->x, &cheeses->y) != 2){
|
|
fprintf(stderr, "error: failed to load clicker save data.\n");
|
|
fclose(f);
|
|
return;
|
|
}
|
|
for(int i = 0; i < n; i++){
|
|
if(fscanf(f, "%lf\n%d\n%lf\n%d\n",
|
|
&workers[i].price.x, &workers[i].price.y,
|
|
&workers[i].owned.x, &workers[i].owned.y) != 4){
|
|
fprintf(stderr, "error: failed to load clicker save data.\n");
|
|
break;
|
|
}
|
|
}
|
|
for(int i = 0; i < n2; i++){
|
|
if(fscanf(f, "%lf\n%d\n", &prize[i].price.x, &prize[i].price.y) != 2){
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
//i should make a version of this that just saves a big string
|
|
//so i can use many
|
|
//like generate the "save file" that returns a pointer to a char
|
|
//and then propersave(*thatchar, "file.txt") and it makes file.txt
|
|
//and it saves *thatchar to it idk this seems like a job for claude
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum { SHOP_WORKERS, SHOP_PRIZES } shopmode;
|
|
|
|
static void draweverything(WINDOW *win, bignum cheeses, ratworker workers[], prizes prize[], int n, int n2,
|
|
int currentworker, int currentprize, shopmode active){
|
|
drawwin(win, cheeses, workers, n);
|
|
drawshop(win, workers, n, currentworker, cheeses, active == SHOP_WORKERS);
|
|
drawpshop(win, prize, n2, currentprize, cheeses, active == SHOP_PRIZES);
|
|
wrefresh(win);
|
|
}
|
|
|
|
|
|
#define spacedebouncems 20
|
|
|
|
static long nowms(void) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
|
|
}
|
|
|
|
void gameclicker(){
|
|
|
|
srand(time(NULL));
|
|
|
|
WINDOW *win = ratdrawbox();
|
|
mousemask(BUTTON1_PRESSED, NULL);
|
|
mouseinterval(0); //don't wait to resolve single vs double click
|
|
|
|
running = 1;
|
|
|
|
bignum cheeses = {0.0, 0};
|
|
bignum onecheese = {1.0, 0}; //what a single click is worth
|
|
int currentworker = 0;
|
|
int currentprize = 0;
|
|
shopmode activeshop = SHOP_WORKERS;
|
|
|
|
static ratworker workers[nhowmanytiers] = {
|
|
//name, bignum price,clicks/tick,multiplier,owned (howmanyowned)
|
|
{ "small rat", {10.0, 0}, {1.0, 0}, {1.15, 0}, {0.0, 0} },
|
|
{ "brown rat", {20.0, 0}, {2.0, 0}, {1.15, 0}, {0.0, 0} },
|
|
{ "fancy rat", {25.0, 0}, {4.0, 0}, {1.14, 0}, {0.0, 0} },
|
|
{ "fat rat", {130.0, 0}, {10.0, 0}, {1.13, 0}, {0.0, 0} },
|
|
{ "evil rat", {400.0, 0}, {15.0, 0}, {1.13, 0}, {0.0, 0} },
|
|
{ "rat farm", {1.2, 1}, {25.0, 0}, {1.12, 0}, {0.0, 0} },
|
|
{ "cheese mill", {3.0, 1}, {30.0, 0}, {1.11, 0}, {0.0, 0} },
|
|
{ "dairy factory", {9.0, 1}, {50.0, 0}, {1.11, 0}, {0.0, 0} },
|
|
{ "cheese research institute", {9.5, 1}, {80.0, 0}, {1.05, 0}, {0.0, 0} },
|
|
{ "cheese accelerator", {30.0, 1}, {100.0, 0}, {1.11, 0}, {0.0, 0} },
|
|
{ "the cheese singularity", {120.0, 1}, {250.0, 0}, {1.10, 0}, {0.0, 0} },
|
|
};
|
|
|
|
static prizes prize[nhowmanyprizes] = {
|
|
{"solid cheese", {100.0, 0}, {1.15, 0}, "hunger", 2},
|
|
{"cheese mountain", {5000.0, 0}, {1.15, 0}, "hunger", 8},
|
|
{"cheese wheel", {500.0, 0}, {1.15, 0}, "health", 1},
|
|
{"medical cheese", {8000.0, 0}, {1.15, 0}, "health", 5},
|
|
{"cheese fountain", {2000.0, 0}, {1.15, 0}, "love", 3},
|
|
{"love cheese cake", {15000.0, 0}, {1.15, 0}, "love", 7},
|
|
{"squeaky cheese", {300.0, 0}, {1.15, 0}, "fun", 2},
|
|
{"party cheese", {12000.0, 0}, {1.15, 0}, "fun", 6},
|
|
{"cheese soap", {400.0, 0}, {1.15, 0}, "clean", 2},
|
|
{"cheese bubbles", {9000.0, 0}, {1.15, 0}, "clean", 5}
|
|
};
|
|
|
|
|
|
clickerload(&cheeses, workers, nhowmanytiers, prize, nhowmanyprizes);
|
|
|
|
draweverything(win, cheeses, workers, prize, nhowmanytiers, nhowmanyprizes, currentworker, currentprize, activeshop);
|
|
|
|
time_t lasttime = time(NULL);
|
|
static int spaceheld = 0;
|
|
static long lastspacetime = 0;
|
|
while(running){
|
|
|
|
int ch;
|
|
while((ch = getch()) != ERR){ //drain the whole queue each frame so rapid clicks/keys aren't ignored
|
|
switch (ch) {
|
|
case 'q': case 'Q': running = 0; break;
|
|
case '\t':
|
|
activeshop = (activeshop == SHOP_WORKERS) ? SHOP_PRIZES : SHOP_WORKERS;
|
|
break;
|
|
case KEY_UP:
|
|
if(activeshop == SHOP_WORKERS){
|
|
if(currentworker > 0) currentworker--;
|
|
} else {
|
|
if(currentprize > 0) currentprize--;
|
|
}
|
|
break;
|
|
case KEY_DOWN:
|
|
if(activeshop == SHOP_WORKERS){
|
|
if(currentworker < nhowmanytiers - 1) currentworker++;
|
|
} else {
|
|
if(currentprize < nhowmanyprizes - 1) currentprize++;
|
|
}
|
|
break;
|
|
case '\n': case KEY_ENTER:
|
|
if(activeshop == SHOP_WORKERS)
|
|
buyworker(&cheeses, &workers[currentworker]);
|
|
else
|
|
buyprize(&cheeses, &prize[currentprize]);
|
|
break;
|
|
case ' ': {
|
|
long now = nowms();
|
|
if(!spaceheld){
|
|
cheeses = addbignum(cheeses, onecheese);
|
|
spaceheld = 1;
|
|
}
|
|
lastspacetime = now;
|
|
break;
|
|
}
|
|
case KEY_MOUSE: {
|
|
MEVENT event;
|
|
if(getmouse(&event) == OK && (event.bstate & BUTTON1_PRESSED)){
|
|
int y = event.y, x = event.x;
|
|
//translate the screen click into winlocal coordinates
|
|
if(wmouse_trafo(win, &y, &x, FALSE)){
|
|
if (y >= artrow0 && y < artrow0 + artrows &&
|
|
x >= artcol0 && x < artcol0 + artcols){
|
|
cheeses = addbignum(cheeses, onecheese);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(!running) break;
|
|
}
|
|
|
|
if(spaceheld && (nowms() - lastspacetime) > spacedebouncems){
|
|
spaceheld = 0;
|
|
}
|
|
|
|
time_t currenttime = time(NULL);
|
|
if(currenttime >= lasttime +1){
|
|
cheeses = addbignum(cheeses, calculatetotalincome(workers, nhowmanytiers));
|
|
cheeses = clampnonneg(cheeses);
|
|
lasttime = currenttime;
|
|
}
|
|
|
|
if(running) draweverything(win, cheeses, workers, prize, nhowmanytiers, nhowmanyprizes, currentworker, currentprize, activeshop);
|
|
|
|
usleep(16667); //60 fps according to google ai overview lmao
|
|
}
|
|
clickersave(cheeses, workers, nhowmanytiers, prize, nhowmanyprizes);
|
|
stop();
|
|
}
|