#include #include #include #include #include #include extern const char *__progname; #include "mjp.h" #include "client.h" #include "strings.h" #include "pollloop.h" #if NWIND != 4 #error Code needs updating #endif #define NPLAYERS 4 #define PL_B 0 #define PL_L 1 #define PL_R 2 #define PL_T 3 #if NPLAYERS != NWIND #error code needs updating #endif static unsigned char ccc_wallpresent[WALLSIZE]; static unsigned char ccc_replpresent[REPLSIZE]; static unsigned char ccc_dorapresent[DORASIZE]; static TILE ccc_doratile[DORASIZE]; static char *ccc_windnames[NWIND]; static int ccc_mywind; static int ccc_nhand[NWIND]; static TILE ccc_hand[NWIND][MAXHAND]; static TILE sorted_hand[NWIND][MAXHAND]; static int ccc_nmeld[NWIND]; static MELD ccc_melds[NWIND][MAXMELDS]; static int ccc_ndiscard[NWIND]; static TILE ccc_discard[NWIND][DECKSIZE]; static int ccc_nfs[NWIND]; static TILE ccc_fs[NWIND][MAXFS]; static int ccc_riichi_at[NWIND]; typedef enum { ACT_WAIT = 1, ACT_PLAY, ACT_CLAIM } ACTIVITY; typedef struct chathist CHATHIST; typedef struct chatline CHATLINE; struct chathist { int nlines; CHATLINE *lines; } ; struct chatline { CHATLINE *link; int src; unsigned int dstmask; char *text; int len; int lines; } ; static int windfor[NPLAYERS]; #define W_L (windfor[PL_L]) #define W_R (windfor[PL_R]) #define W_T (windfor[PL_T]) #define W_B (windfor[PL_B]) static int playerfor[NWIND]; static char plndisp[NPLAYERS][24]; static int wallshift; static int stdinid; static int updid; static int want_speech; static int speaking; static unsigned int speakto; static char *speakbuf; static int speakba; static int speakbl; static int speakoff; static CHATHIST chist; static int want_chat; static int want_board; static int curs_y; static int curs_x; static ACTIVITY activity; static ACTIVITY ccc_act; static ACTIVITY reject_activity; static int act_cursor; static int act_flag[MAXHAND]; static int refreshid; static int want_refresh; static char *modeline_msg; static int modeline_len; static int modeline_id; static int modeline_stayup; #define MODELINE_STAY_TIME 0x00000001 #define MODELINE_STAY_KEY 0x00000002 static struct timeval modeline_timeout; void client_original_args(int ac __attribute__((__unused__)), const char * const *av __attribute__((__unused__))) { } int client_flag(const char *flag __attribute__((__unused__))) { return(CLIENT_FLAG_UNKNOWN); } void client_args(const char * const *args __attribute__((__unused__))) { abort(); } static void start_speech(void) { want_speech = 1; speaking = 1; speakbl = 0; speakto = 0; speakoff = 0; } static void to_hand_loc(int p, int x) { switch (p) { case PL_B: move(19,57-(3*x)); break; case PL_T: move(1,57-(3*x)); break; case PL_L: move(14-(x/2),3-(3*(x&1))); break; case PL_R: move(14-(x/2),76-(3*(x&1))); break; default: abort(); break; } } static const char *tile_name(TILE t) { switch (t) { case TILE_UNKTILE: return("?""?"); break; case TILE_NOTILE: return(".."); break; } if ((t < 0) || (t >= TILE__N)) abort(); return(tilenames[t]); } static void display_hand_tile(int w, int x, int marked, int so) { to_hand_loc(playerfor[w],x); printw("%c",marked?'<':' '); if (so) standout(); printw("%-2s",tile_name(sorted_hand[w][x])); if (so) standend(); printw("%c",marked?'>':' '); } static void move_act_cursor(int inc) { display_hand_tile(mywind,act_cursor,0,(activity==ACT_CLAIM)&&act_flag[act_cursor]); act_cursor += inc; if (act_cursor >= nhand[mywind]) act_cursor = nhand[mywind] - 1; if (act_cursor < 0) act_cursor = 0; display_hand_tile(mywind,act_cursor,1,(activity==ACT_CLAIM)&&act_flag[act_cursor]); want_refresh = 1; } static void claim_claim(void) { int i; int n; TILE t[3]; if (ccc_act != ACT_CLAIM) { beep(); return; } n = 0; for (i=nhand[mywind]-1;i>=0;i--) { if (act_flag[i]) { if (n < 3) t[n] = sorted_hand[mywind][i]; n ++; } } if ((n < 2) || (n > 3)) { beep(); return; } if (n == 2) t[2] = TILE_NOTILE; server_claim(t[0],t[1],t[2]); reject_activity = ACT_CLAIM; activity = ACT_WAIT; } static void play_discard(void) { if (ccc_act != ACT_PLAY) { beep(); return; } if ( (riichi_at[mywind] >= 0) && (act_cursor != nhand[mywind]-1) ) { beep(); return; } server_discard(sorted_hand[mywind][act_cursor]); reject_activity = ACT_PLAY; activity = ACT_WAIT; } static void play_kong(void) { TILE t; int i; int n; if (ccc_act != ACT_CLAIM) { beep(); return; } t = sorted_hand[mywind][act_cursor]; n = 0; for (i=ccc_nhand[mywind]-1;i>=0;i--) if (ccc_hand[mywind][i] == t) n ++; if (n == 4) { server_ckong(t); reject_activity = ACT_CLAIM; activity = ACT_WAIT; return; } for (i=ccc_nmeld[mywind]-1;i>=0;i--) { if ( (melds[mywind][i].ntiles == 3) && (melds[mywind][i].tiles[0] == t) && (melds[mywind][i].tiles[1] == t) && (melds[mywind][i].tiles[2] == t) ) { server_pkong(t); reject_activity = ACT_CLAIM; activity = ACT_WAIT; return; } } beep(); } static void playclaim_out(void) { switch (ccc_act) { case ACT_PLAY: server_out(); reject_activity = ACT_PLAY; activity = ACT_WAIT; break; case ACT_CLAIM: server_ron(); reject_activity = ACT_CLAIM; activity = ACT_WAIT; break; default: beep(); break; } } static void claim_pass(void) { if (ccc_act != ACT_CLAIM) { beep(); return; } server_pass(); reject_activity = ACT_CLAIM; activity = ACT_WAIT; } static void play_riichi(void) { if (ccc_act != ACT_PLAY) { beep(); return; } if (riichi_at[mywind] >= 0) { beep(); return; } server_discard_riichi(sorted_hand[mywind][act_cursor]); reject_activity = ACT_PLAY; activity = ACT_WAIT; } static void claim_swap(void) { if (ccc_act != ACT_CLAIM) { beep(); return; } server_swap(sorted_hand[mywind][act_cursor]); reject_activity = ACT_CLAIM; activity = ACT_WAIT; } static void typed_char(int c) { if (dbgon()) { dbg("typed char: %02x\n",c); dbgflush(); } switch (speaking) { default: abort(); break; case 0: if (modeline_msg) modeline_stayup &= ~MODELINE_STAY_KEY; switch (c) { case '\e': if (modeline_msg) { free(modeline_msg); remove_block_id(modeline_id); modeline_msg = 0; want_speech = 1; } break; case '"': case '\'': start_speech(); speakto = 15; speaking = 2; break; case '@': start_speech(); break; case 'h': { int inc; inc = 1; if (0) { case 'l': inc = -1; } switch (ccc_act) { case ACT_PLAY: case ACT_CLAIM: move_act_cursor(inc); break; default: beep(); break; } } break; case ' ': switch (ccc_act) { case ACT_CLAIM: act_flag[act_cursor] = ! act_flag[act_cursor]; move_act_cursor(0); break; default: beep(); break; } break; case 'C': claim_claim(); break; case 'D': play_discard(); break; case 'K': play_kong(); break; case 'O': playclaim_out(); break; case 'P': claim_pass(); break; case 'R': play_riichi(); break; case 'S': claim_swap(); break; default: beep(); break; } break; case 1: switch (c) { default: beep(); break; case 'e': case 'E': speakto |= 1 << WIND_E; break; case 's': case 'S': speakto |= 1 << WIND_S; break; case 'w': case 'W': speakto |= 1 << WIND_W; break; case 'n': case 'N': speakto |= 1 << WIND_N; break; case '\e': speaking = 0; break; case '\r': case '\n': if (! speakto) { beep(); speaking = 0; } else { speaking = 2; } break; } want_speech = 1; break; case 2: switch (c) { case '\b': case '\177': if (speakbl > 0) speakbl --; break; case '\e': speaking = 0; break; case '\r': case '\n': server_chat(speakto,speakbuf,speakbl); speaking = 0; break; default: if ( (c < 32) || ((c > 126) && (c < 160)) || (speakbl >= MJP_MAXCHATLEN) ) { beep(); } else { if (speakbl >= speakba) { speakbuf = realloc(speakbuf,speakba=speakbl+16); } speakbuf[speakbl++] = c; } break; } want_speech = 1; break; } } static void rd_stdin(void *arg __attribute__((__unused__))) { unsigned char ibuf[256]; int r; int i; r = read(0,&ibuf[0],sizeof(ibuf)); if (r < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } fprintf(stderr,"%s: stdin read: %s\n",__progname,strerror(errno)); exit(1); } for (i=0;i speakbl-10) { speakoff = speakbl - 65; } else if (speakoff < speakbl-72) { speakoff = speakbl - 25; } if (speakoff < 0) speakoff = 0; if (speakto == 15) printw("all"); else printw("%s",speech_dst_str(speakto)); printw(": "); if (speakoff > 0) printw("<"); printw("%.*s",speakbl-speakoff,speakbuf+speakoff); clrtoeol(); curs_x = getcurx(stdscr); break; } } static void chathist_init(CHATHIST *h) { h->nlines = 0; h->lines = 0; } static void chathist_append(CHATHIST *h, int from, unsigned int others, const char *text, int len) { CHATLINE *l; l = malloc(sizeof(CHATLINE)); l->src = from; l->dstmask = others | (1 << mywind); l->len = asprintf( &l->text, "%c->%s: %.*s", windchar(from), (l->dstmask==15)?"all":speech_dst_str(l->dstmask), len, text ); if (l->len <= 79) { l->lines = 1; } else { l->lines = (l->len-2 + 77-1) / 77; } if (dbgon()) { dbg("chathist_append: from=%d others=%u text=%.*s\n",from,others,len,text); dbg("chathist_append: l->len=%d l->lines=%d l->text=%s\n",l->len,l->lines,l->text); dbgflush(); } l->link = h->lines; h->lines = l; h->nlines ++; } static void chatline_lines(CHATLINE *l, void (*ln)(const char *txt, int)) { char linebuf[79]; int x; int n; if (l->lines < 2) { (*ln)(l->text,l->len); return; } (*ln)(l->text,79); linebuf[0] = '+'; linebuf[1] = ' '; x = 79; while (1) { n = l->len - x; if (n < 0) abort(); if (n == 0) break; if (n > 77) n = 77; bcopy(l->text+x,&linebuf[2],n); (*ln)(&linebuf[0],n+2); x += n; } } static void redraw_chat(void) { int y; int yy; CHATLINE *l; void ln(const char *txt, int len) { if (yy >= 20) { move(yy,0); printw("%.*s",len,txt); clrtoeol(); } yy ++; } y = 23; l = chist.lines; while ((y > 20) && l) { y -= l->lines; yy = y; chatline_lines(l,&ln); l = l->link; } } static void gen_plndisp(void) { int n; int l; sprintf(&plndisp[PL_L][0],"%-23.20s",windnames[W_L]); sprintf(&plndisp[PL_R][0],"%23.20s",windnames[W_R]); n = strlen(windnames[W_T]); if (n > 20) n = 20; l = (23 - (n+3)) / 2; sprintf(&plndisp[PL_T][0],"%*s%.*s %c%*s", l,"",n,windnames[W_T],windchar(W_T),23-(l+n+3),""); n = strlen(windnames[W_B]); if (n > 20) n = 20; l = (23 - (n+3)) / 2; sprintf(&plndisp[PL_B][0],"%*s%.*s %c%*s", l,"",n,windnames[W_B],windchar(W_B),23-(l+n+3),""); } static void draw_names_and_winds(void) { mvprintw(0,0,"%s %s %s", &plndisp[PL_L][0],&plndisp[PL_T][0],&plndisp[PL_R][0]); clrtoeol(); mvaddch(3,1,windchar(W_L)); mvaddch(3,77,windchar(W_R)); mvprintw(16,28,"%s",&plndisp[PL_B][0]); } static void to_wall_loc(int wx) { int xwp; wx = (wx + wallshift) % WALLSIZE; xwp = wx % (WALLSIZE/4); switch (wx/(WALLSIZE/4)) { case 0: move(17+(xwp&1),63-(3*(xwp/2))); break; case 1: move(18-(xwp/2),11-(3*(xwp&1))); break; case 2: move(3-(xwp&1),15+(3*(xwp/2))); break; case 3: move(2+(xwp/2),67+(3*(xwp&1))); break; default: abort(); break; } } static void to_repl_loc(int rx) { move(9+(rx&1),23+(3*(rx/2))); } static void to_dora_loc(int rx) { move(9+(rx&1),55-(3*(rx/2))); } static void update_walls(void) { int i; for (i=WALLSIZE-1;i>=0;i--) { if (wallpresent[i] != ccc_wallpresent[i]) { to_wall_loc(i); addstr(wallpresent[i]?"?""?":".."); ccc_wallpresent[i] = wallpresent[i]; } } for (i=REPLSIZE-1;i>=0;i--) { if (replpresent[i] != ccc_replpresent[i]) { to_repl_loc(i); addstr(replpresent[i]?tile_name(TILE_UNKTILE):".."); ccc_replpresent[i] = replpresent[i]; } } for (i=DORASIZE-1;i>=0;i--) { if ( (dorapresent[i] != ccc_dorapresent[i]) || (dorapresent[i] && (doratile[i] != ccc_doratile[i])) ) { to_dora_loc(i); addstr(dorapresent[i]?tile_name(doratile[i]):".."); ccc_dorapresent[i] = dorapresent[i]; ccc_doratile[i] = doratile[i]; } } } static int tilevecs_differ(TILE *v1, TILE *v2, int n) { for (;n>0;n--) if (*v1++ != *v2++) return(1); return(0); } static int tilerank(TILE t) { static const int r[TILE__N] = { [TILE_1_BAMBOO] = 42, [TILE_1_CHARACTER] = 33, [TILE_1_DOT] = 24, [TILE_2_BAMBOO] = 41, [TILE_2_CHARACTER] = 32, [TILE_2_DOT] = 23, [TILE_3_BAMBOO] = 40, [TILE_3_CHARACTER] = 31, [TILE_3_DOT] = 22, [TILE_4_BAMBOO] = 39, [TILE_4_CHARACTER] = 30, [TILE_4_DOT] = 21, [TILE_5_BAMBOO] = 38, [TILE_5_CHARACTER] = 29, [TILE_5_DOT] = 20, [TILE_6_BAMBOO] = 37, [TILE_6_CHARACTER] = 28, [TILE_6_DOT] = 19, [TILE_7_BAMBOO] = 36, [TILE_7_CHARACTER] = 27, [TILE_7_DOT] = 18, [TILE_8_BAMBOO] = 35, [TILE_8_CHARACTER] = 26, [TILE_8_DOT] = 17, [TILE_9_BAMBOO] = 34, [TILE_9_CHARACTER] = 25, [TILE_9_DOT] = 16, [TILE_WIND_E] = 15, [TILE_FLOWER_A] = 0, [TILE_SEASON_A] = 0, [TILE_WIND_S] = 14, [TILE_FLOWER_B] = 0, [TILE_SEASON_B] = 0, [TILE_WIND_W] = 13, [TILE_FLOWER_C] = 0, [TILE_SEASON_C] = 0, [TILE_WIND_N] = 12, [TILE_FLOWER_D] = 0, [TILE_SEASON_D] = 0, [TILE_DRAGON_R] = 11, [TILE_DRAGON_G] = 10, [TILE_DRAGON_W] = 9, [TILE_HAKU] = 8 }; switch (t) { case TILE_NOTILE: return(43); break; case TILE_UNKTILE: return(44); break; } if ((t < 0) || (t >= TILE__N)) abort(); return(r[t]); } static void sort_hand(TILE *v, int n) { int i; int j; TILE t; for (j=n-1;j>0;j--) { for (i=j-1;i>=0;i--) { if (tilerank(v[i]) > tilerank(v[j])) { t = v[j]; v[j] = v[i]; v[i] = t; } } } } static void redisplay_sorted_hand(int w) { int i; for (i=nhand[w]-1;i>=0;i--) display_hand_tile(w,i,0,0); if (w == mywind) { switch (ccc_act) { default: abort(); break; case ACT_WAIT: break; case ACT_PLAY: display_hand_tile(w,act_cursor,1,0); break; case ACT_CLAIM: for (i=nhand[w]-1;i>=0;i--) { if (act_flag[i]) display_hand_tile(w,i,0,1); } display_hand_tile(w,act_cursor,1,act_flag[act_cursor]); break; } } } static void update_hand(int w) { int i; if (ccc_nhand[w] < 0) ccc_nhand[w] = 0; for (i=ccc_nhand[w]-1;i>=nhand[w];i--) { to_hand_loc(playerfor[w],i); printw(" "); } bcopy(&hand[w][0],&sorted_hand[w][0],nhand[w]*sizeof(TILE)); i = nhand[w]; if ((riichi_at[w] >= 0) || (activity == ACT_PLAY)) i --; sort_hand(&sorted_hand[w][0],i); redisplay_sorted_hand(w); bcopy(&hand[w][0],&ccc_hand[w][0],nhand[w]*sizeof(TILE)); ccc_nhand[w] = nhand[w]; } static void fs_mask(TILE t, unsigned int *fmp, unsigned int *smp) { switch (t) { case TILE_FLOWER_A: *fmp |= 1; break; case TILE_FLOWER_B: *fmp |= 2; break; case TILE_FLOWER_C: *fmp |= 4; break; case TILE_FLOWER_D: *fmp |= 8; break; case TILE_SEASON_A: *smp |= 1; break; case TILE_SEASON_B: *smp |= 2; break; case TILE_SEASON_C: *smp |= 4; break; case TILE_SEASON_D: *smp |= 8; break; default: abort(); break; } } static void print_fs_mask(void (*to)(int), int p, char c, unsigned int m) { (*to)(p); printw("%c%c%c%c%c",c,(m&1)?'1':'-',(m&2)?'2':'-',(m&4)?'3':'-',(m&8)?'4':'-'); } static void to_f_loc(int p) { switch (p) { case PL_B: move(19,12); break; case PL_T: move(1,12); break; case PL_L: move(16,1); break; case PL_R: move(16,74); break; default: abort(); break; } } static void to_s_loc(int p) { switch (p) { case PL_B: move(19,63); break; case PL_T: move(1,63); break; case PL_L: move(17,1); break; case PL_R: move(17,74); break; default: abort(); break; } } static void update_fs(int w) { int p; int i; unsigned int fmask; unsigned int smask; p = playerfor[w]; if (ccc_nfs[w] < 0) ccc_nfs[w] = 0; if (nfs[w] < ccc_nfs[w]) abort(); fmask = 0; smask = 0; for (i=nfs[w]-1;i>=0;i--) fs_mask(fs[w][i],&fmask,&smask); if (fmask) print_fs_mask(&to_f_loc,p,'f',fmask); if (smask) print_fs_mask(&to_s_loc,p,'s',smask); ccc_nfs[w] = nfs[w]; } static void to_discard_loc(int p, int x) { switch (p) { case PL_B: move(15-(x/10),53-(3*(x%10))); break; case PL_T: move(4+(x/10),23+(3*(x%10))); break; case PL_L: move(15-(x%11),14+(3*(x/11))); break; case PL_R: move(5+(x%11),62-(3*(x/11))); break; default: abort(); break; } } static void display_discard_tile(int p, int x, int mark, int so) { int w; if (x < 0) abort(); w = windfor[p]; to_discard_loc(p,x); printw("%c",mark?'(':' '); if (so) standout(); printw("%-2s",(x>=ndiscard[w])?" ":tile_name(discard[w][x])); if (so) standend(); printw("%c",mark?')':' '); } static void update_discard(int w) { int p; int i; p = playerfor[w]; if (dbgon()) { dbg("update_discard: w=%d riichi_at[w]=%d\n",w,riichi_at[w]); dbgflush(); } if (ccc_ndiscard[w] < 0) ccc_ndiscard[w] = 0; for (i=ccc_ndiscard[w]-1;i>=ndiscard[w];i--) display_discard_tile(p,i,0,0); for (i=ndiscard[w]-1;i>=0;i--) { if ((i >= ccc_ndiscard[w]) || (discard[w][i] != ccc_discard[w][i])) { if (i != riichi_at[w]) { display_discard_tile(p,i,0,0); } ccc_discard[w][i] = discard[w][i]; } } if (riichi_at[w] >= 0) display_discard_tile(p,riichi_at[w],1,0); ccc_ndiscard[w] = ndiscard[w]; ccc_riichi_at[w] = riichi_at[w]; } static void to_meld_start(int p, int *rp, int *cp) { switch (p) { case PL_B: *rp = 19; *cp = 19; break; case PL_T: *rp = 1; *cp = 19; break; case PL_L: *rp = 5; *cp = 1; break; case PL_R: *rp = 5; *cp = 73; break; default: abort(); break; } } static void show_meld(int p, int *rp, int *cp, int mn, TILE *mv) { int i; switch (mn) { case 3: case 4: break; default: abort(); break; } move(*rp,*cp); switch (p) { case PL_B: case PL_T: /* XXX printw() is broken; it's documented as being printf-like, but it doesn't return the number of characters printed. */ for (i=0;i=0;i--) { show_meld(p,&r,&c,melds[w][i].ntiles,&melds[w][i].tiles[0]); ccc_melds[w][i] = melds[w][i]; } ccc_nmeld[w] = nmeld[w]; } static void redraw_board(void) { int i; int j; if (mywind != ccc_mywind) { ccc_mywind = mywind; switch (mywind) { case WIND_E: wallshift = 0; break; case WIND_S: wallshift = 102; break; case WIND_W: wallshift = 68; break; case WIND_N: wallshift = 34; break; default: abort(); break; } windfor[PL_B] = mywind; windfor[PL_L] = WIND_NEXT(mywind); windfor[PL_T] = WIND_NEXT(windfor[PL_L]); windfor[PL_R] = WIND_NEXT(windfor[PL_T]); for (i=NPLAYERS-1;i>=0;i--) playerfor[windfor[i]] = i; for (i=WALLSIZE-1;i>=0;i--) ccc_wallpresent[i] = 2; for (i=REPLSIZE-1;i>=0;i--) ccc_replpresent[i] = 2; for (i=DORASIZE-1;i>=0;i--) ccc_dorapresent[i] = 2; for (i=NWIND-1;i>=0;i--) { ccc_windnames[i] = 0; ccc_nhand[i] = -1; ccc_nmeld[i] = -1; ccc_ndiscard[i] = -1; ccc_nfs[i] = -1; ccc_riichi_at[i] = -2; } } for (i=NWIND-1;i>=0;i--) { if (windnames[i] != ccc_windnames[i]) { for (j=NWIND-1;j>=0;j--) ccc_windnames[j] = windnames[j]; gen_plndisp(); draw_names_and_winds(); break; } } update_walls(); for (i=NWIND-1;i>=0;i--) { if ( (nhand[i] != ccc_nhand[i]) || tilevecs_differ(&hand[i][0],&ccc_hand[i][0],nhand[i]) ) { update_hand(i); } if ( (nfs[i] != ccc_nfs[i]) || tilevecs_differ(&fs[i][0],&ccc_fs[i][0],nfs[i]) ) { update_fs(i); } if ( (ndiscard[i] != ccc_ndiscard[i]) || (riichi_at[i] != ccc_riichi_at[i]) || tilevecs_differ(&discard[i][0],&ccc_discard[i][0],ndiscard[i]) ) { update_discard(i); } if ( (nmeld[i] != ccc_nmeld[i]) || ({ for (j=nmeld[i]-1;j>=0;j--) { if (melds[i][j].ntiles != ccc_melds[i][j].ntiles) break; if (tilevecs_differ(&melds[i][j].tiles[0],&ccc_melds[i][j].tiles[0],melds[i][j].ntiles)) break; } (j>=0); }) ) { update_melds(i); } } } static void display_last_discard(int mark) { if (last_discard_to < 0) return; if (ndiscard[last_discard_to] < 1) abort(); display_discard_tile( playerfor[last_discard_to], ndiscard[last_discard_to]-1, riichi_at[last_discard_to]==ndiscard[last_discard_to]-1, mark ); } static int update(void *arg __attribute__((__unused__))) { int rv; int i; rv = BLOCK_NIL; if (want_board) { want_board = 0; redraw_board(); rv = BLOCK_LOOP; } if (want_speech) { want_speech = 0; redraw_speech(); rv = BLOCK_LOOP; } if (want_chat) { want_chat = 0; redraw_chat(); rv = BLOCK_LOOP; } if (activity != ccc_act) { switch (ccc_act) { default: abort(); break; case ACT_WAIT: break; case ACT_PLAY: display_hand_tile(mywind,act_cursor,0,0); break; case ACT_CLAIM: for (i=ccc_nhand[mywind]-1;i>=0;i--) { if (act_flag[i]) display_hand_tile(mywind,i,0,0); } display_hand_tile(mywind,act_cursor,0,0); if (last_discard_to >= 0) { if (ccc_ndiscard[last_discard_to] < 1) abort(); display_last_discard(0); } break; } ccc_act = activity; switch (activity) { default: abort(); break; case ACT_WAIT: break; case ACT_PLAY: act_cursor = (riichi_at[mywind] >= 0) ? nhand[mywind]-1 : nhand[mywind]/2; display_hand_tile(mywind,act_cursor,1,0); break; case ACT_CLAIM: act_cursor = nhand[mywind] / 2; for (i=MAXHAND-1;i>=0;i--) act_flag[i] = 0; display_hand_tile(mywind,act_cursor,1,0); if (last_discard_to >= 0) { if (ccc_ndiscard[last_discard_to] < 1) abort(); display_last_discard(1); } break; } want_speech = 1; rv = BLOCK_LOOP; } if (rv == BLOCK_LOOP) want_refresh = 1; return(rv); } static int maybe_refresh(void *arg __attribute__((__unused__))) { if (! want_refresh) return(BLOCK_NIL); want_refresh = 0; move(curs_y,curs_x); refresh(); return(BLOCK_LOOP); } void client_startup(void) { initscr(); clear(); leaveok(stdscr,FALSE); noecho(); cbreak(); speaking = 0; speakbuf = 0; speakba = 0; want_speech = 1; want_chat = 1; want_board = 1; stdinid = add_poll_fd(0,&rwtest_always,&rwtest_never,&rd_stdin,0,0); updid = add_block_fn(&update,0); want_refresh = 1; refreshid = add_block_fn(&maybe_refresh,0); ccc_mywind = WIND_BAD; chathist_init(&chist); activity = ACT_WAIT; ccc_act = ACT_WAIT; } void client_chat(int from, unsigned int otherdest, const char *text, int len) { chathist_append(&chist,from,otherdest,text,len); want_chat = 1; } void client_change_notify(void) { want_board = 1; } void client_play(void) { if (activity != ACT_WAIT) abort(); activity = ACT_PLAY; } void client_claim(void) { if (activity != ACT_WAIT) abort(); activity = ACT_CLAIM; } static int modeline_block(void *arg __attribute__((__unused__))) { if (modeline_stayup & MODELINE_STAY_TIME) { struct timeval now; gettimeofday(&now,0); if ( (now.tv_sec > modeline_timeout.tv_sec) || ( (now.tv_sec == modeline_timeout.tv_sec) && (now.tv_usec >= modeline_timeout.tv_usec) ) ) { modeline_stayup &= ~MODELINE_STAY_TIME; } else { return( ((modeline_timeout.tv_sec - now.tv_sec) * 1000) + ((modeline_timeout.tv_usec+1000000-now.tv_usec)/1000) - 999 ); } } if (modeline_stayup == 0) { free(modeline_msg); modeline_msg = 0; remove_block_id(modeline_id); want_speech = 1; return(BLOCK_LOOP); } return(BLOCK_NIL); } static void set_modeline_msg(const char *text, int len) { if (modeline_msg) { free(modeline_msg); remove_block_id(modeline_id); } if (text == 0) { modeline_msg = 0; return; } modeline_msg = malloc(len); bcopy(text,modeline_msg,len); modeline_len = len; modeline_stayup = MODELINE_STAY_TIME | MODELINE_STAY_KEY; modeline_id = add_block_fn(&modeline_block,0); gettimeofday(&modeline_timeout,0); modeline_timeout.tv_sec += 5; want_speech = 1; } void client_reject(const char *msg, int len) { activity = reject_activity; set_modeline_msg(msg,len); } const char *client_name(void) { return("curses client"); } /* playername...(0).................playername..S.......................playername. ............f1234..mmmmmmmm.hh.hh.hh.hh.hh.hh.hh.hh.hh.hh.hh...s1234............ ........??.??..??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??..??.??........ E..(3)..??.??..??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??..??.??......W. ........??.??...........dd.dd.dd.dd.dd.dd.dd.dd.dd.dd..............??.??........ .mmmmmm.??.??..dd.dd....dd.dd.dd.dd.dd.dd.dd.dd.dd.dd....dd.dd.dd..??.??.mmmmmm. .mmmmmm.??.??..dd.dd....dd.dd.dd.dd......................dd.dd.dd..??.??.mmmmmm. ........??.??..dd.dd........................................dd.dd..??.??........ .hh.hh..??.??..dd.dd........................................dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd...??.??.??.??.??.??...??.??.??.??.??...dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd...??.??.??.??.??.??...??.??.??.??.??...dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd........30^.................50^.........dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd........................................dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd.........................dd.dd.dd.dd....dd.dd..??.??..hh.hh. .hh.hh..??.??..dd.dd.dd....dd.dd.dd.dd.dd.dd.dd.dd.dd.dd....dd.dd..??.??..hh.hh. ........??.??..dd.dd.dd....dd.dd.dd.dd.dd.dd.dd.dd.dd.dd....dd.dd..??.??........ .f1234..??.??...(16)..............playername..N....................??.??..f1234. .s1234..??.??..??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??..??.??..s1234. ........??.??..??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??.??..??.??........ .(19).......f1234..mmmmmmmm.hh.hh.hh.hh.hh.hh.hh.hh.hh.hh.hh...s1234............ .............................................................63^...^67.......... ................................................................................ ................................................................................ ................................................................................ */