/* $Id$ */ #include "bbs.h" // PTT-BBS Angel System #ifdef PLAY_ANGEL #define FN_ANGELMSG "angelmsg" void angel_toggle_pause() { if (!HasUserPerm(PERM_ANGEL) || !currutmp) return; currutmp->angelpause ++; currutmp->angelpause %= ANGELPAUSE_MODES; // maintain deprecated value cuser.uflag2 &= ~UF2_ANGEL_OLDMASK; } void angel_parse_nick_fp(FILE *fp, char *nick, int sznick) { char buf[PATHLEN]; // should be in first line rewind(fp); *buf = 0; if (fgets(buf, sizeof(buf), fp)) { // verify first line if (buf[0] == '%' && buf[1] == '%' && buf[2] == '[') { chomp(buf+3); strlcpy(nick, buf+3, sznick); } } } void angel_load_my_fullnick(char *buf, int szbuf) { char fn[PATHLEN]; FILE *fp = NULL; *buf = 0; setuserfile(fn, FN_ANGELMSG); if ((fp = fopen(fn, "rt"))) { angel_parse_nick_fp(fp, buf, szbuf); fclose(fp); } strlcat(buf, "小天使", szbuf); } // cache my angel's nickname static char _myangel[IDLEN+1] = "", _myangel_nick[IDLEN+1] = ""; static time4_t _myangel_touched = 0; static char _valid_angelmsg = 0; void angel_reload_nick() { char reload = 0; char fn[PATHLEN]; time4_t ts = 0; FILE *fp = NULL; fn[0] = 0; // see if we have angel id change (reload whole) if (strcmp(_myangel, cuser.myangel) != 0) { strlcpy(_myangel, cuser.myangel, sizeof(_myangel)); reload = 1; } // see if we need to check file touch date if (!reload && _myangel[0] && _myangel[0] != '-') { sethomefile(fn, _myangel, FN_ANGELMSG); ts = dasht(fn); if (ts != -1 && ts > _myangel_touched) reload = 1; } // if no need to reload, reuse current data. if (!reload) { // vmsg("angel_data: no need to reload."); return; } // reset cache _myangel_touched = ts; _myangel_nick[0] = 0; _valid_angelmsg = 0; // quick check if (_myangel[0] == '-' || !_myangel[0]) return; // do reload data. if (!fn[0]) { sethomefile(fn, _myangel, FN_ANGELMSG); ts = dasht(fn); _myangel_touched = ts; } assert(*fn); // complex load fp = fopen(fn, "rt"); if (fp) { _valid_angelmsg = 1; angel_parse_nick_fp(fp, _myangel_nick, sizeof(_myangel_nick)); fclose(fp); } } const char * angel_get_nick() { angel_reload_nick(); return _myangel_nick; } int t_changeangel(){ char buf[4]; /* cuser.myangel == "-" means banned for calling angel */ if (cuser.myangel[0] == '-' || cuser.myangel[1] == 0) return 0; getdata(b_lines - 1, 0, "更換小天使後就無法換回了喔! 是否要更換小天使? [y/N]", buf, 3, LCECHO); if (buf[0] == 'y') { char buf[100]; snprintf(buf, sizeof(buf), "%s 小主人 %s 換掉 %s 小天使\n", Cdatelite(&now), cuser.userid, cuser.myangel); log_file(BBSHOME "/log/changeangel.log", LOG_CREAT, buf); cuser.myangel[0] = 0; outs("小天使更新完成,下次呼叫時會選出新的小天使"); } return XEASY; } int t_angelmsg(){ char msg[3][74] = { "", "", "" }; char nick[10] = ""; char buf[512]; int i; FILE* fp; setuserfile(buf, "angelmsg"); fp = fopen(buf, "r"); if (fp) { i = 0; if (fgets(msg[0], sizeof(msg[0]), fp)) { chomp(msg[0]); if (strncmp(msg[0], "%%[", 3) == 0) { strlcpy(nick, msg[0] + 3, 7); move(4, 0); prints("原有暱稱:%s小天使", nick); msg[0][0] = 0; } else i = 1; } else msg[0][0] = 0; move(5, 0); outs("原有留言:\n"); if(msg[0][0]) outs(msg[0]); for (; i < 3; ++i) { if(fgets(msg[i], sizeof(msg[0]), fp)) { outs(msg[i]); chomp(msg[i]); } else break; } fclose(fp); } getdata_buf(11, 0, "小天使暱稱:", nick, 7, 1); do { move(12, 0); clrtobot(); outs("不在的時候要跟小主人說什麼呢?" "最多三行,按[Enter]結束"); for (i = 0; i < 3 && getdata_buf(14 + i, 0, ":", msg[i], sizeof(msg[i]), DOECHO); ++i); getdata(b_lines - 2, 0, "(S)儲存 (E)重新來過 (Q)取消?[S]", buf, 4, LCECHO); } while (buf[0] == 'e'); if (buf[0] == 'q') return 0; setuserfile(buf, "angelmsg"); if (msg[0][0] == 0) unlink(buf); else { FILE* fp = fopen(buf, "w"); if (!fp) return 0; if(nick[0]) fprintf(fp, "%%%%[%s\n", nick); for (i = 0; i < 3 && msg[i][0]; ++i) { fputs(msg[i], fp); fputc('\n', fp); } fclose(fp); } return 0; } inline int angel_reject_me(userinfo_t * uin){ // TODO 超級好友怎麼辦? int* iter = uin->reject; int unum; while ((unum = *iter++)) { if (unum == currutmp->uid) { return 1; } } return 0; } static int FindAngel(void){ int nAngel; int i, j; int choose; int trial = 0; userinfo_t *u; do{ nAngel = 0; // since we have many, many angels now, let's ignore angels in angelpause state. j = SHM->currsorted; u = NULL; for (i = 0; i < SHM->UTMPnumber; ++i) { u = &SHM->uinfo[SHM->sorted[j][0][i]]; if ((u->userlevel & PERM_ANGEL) && (!u->angelpause) && (u->mode != DEBUGSLEEPING)) ++nAngel; } if (nAngel == 0) return 0; choose = random() % nAngel + 1; j = SHM->currsorted; for (i = 0; i < SHM->UTMPnumber && choose; ++i) { u = &SHM->uinfo[SHM->sorted[j][0][i]]; if ((u->userlevel & PERM_ANGEL) && (!u->angelpause) && (u->mode != DEBUGSLEEPING)) --choose; } // u should be correct now! No need to check angelpause in this time. // u = &(SHM->uinfo[SHM->sorted[j][0][i-1]]); if (choose == 0 && u && (u->uid != currutmp->uid) && (u->userlevel & PERM_ANGEL) && !angel_reject_me(u) && u->userid[0]){ strlcpy(cuser.myangel, u->userid, sizeof(cuser.myangel)); passwd_sync_update(usernum, &cuser); return 1; } }while(++trial < 5); return 0; } static inline void GotoNewHand(){ char old_board[IDLEN + 1] = ""; int canRead = 1; if (currutmp && currutmp->mode == EDITING) return; // usually crashed as 'assert(currbid == brc_currbid)' if (currboard[0]) { strlcpy(old_board, currboard, IDLEN + 1); currboard = ""; // force enter_board } if (enter_board(BN_NEWBIE) == 0) canRead = 1; if (canRead) Read(); if (canRead && old_board[0]) enter_board(old_board); } static inline void NoAngelFound(const char* msg){ // don't worry about the screen - // it should have been backuped before entering here. grayout(0, b_lines-3, GRAYOUT_DARK); move(b_lines-4, 0); clrtobot(); outs(msg_seperator); move(b_lines-2, 0); if (!msg) msg = "你的小天使現在不在線上"; outs(msg); if (currutmp == NULL || currutmp->mode != EDITING) outs(",請先在新手板上尋找答案或按 Ctrl-P 發問"); if (vmsg("請按任意鍵繼續,若想直接進入新手板發文請按 'y'") == 'y') GotoNewHand(); } static inline void AngelNotOnline(){ char buf[PATHLEN]; FILE *fp; // use cached angel data (assume already called before.) // angel_reload_nick(); if (!_valid_angelmsg) { NoAngelFound(NULL); return; } // valid angelmsg is ready for being loaded. sethomefile(buf, cuser.myangel, FN_ANGELMSG); fp = fopen(buf, "rt"); if (!fp) { // safer NoAngelFound(NULL); return; } clear(); showtitle("小天使留言", BBSNAME); move(4, 0); buf[0] = 0; prints("您的%s小天使現在不在線上", _myangel_nick); outs("\n祂留言給你:\n"); outs(ANSI_COLOR(1;31;44) "☉┬──────────────┤" ANSI_COLOR(37) "" "小天使留言" ANSI_COLOR(31) "├──────────────┬☉" ANSI_RESET "\n"); outs(ANSI_COLOR(1;31) "╭┤" ANSI_COLOR(32) " 小天使 " " " ANSI_COLOR(31) "├╮" ANSI_RESET "\n"); fgets(buf, sizeof(buf), fp); // skip first line: entry for nick while (fgets(buf, sizeof(buf), fp)) { chomp(buf); prints(ANSI_COLOR(1;31) "│" ANSI_RESET "%-74.74s" ANSI_COLOR(1;31) "│" ANSI_RESET "\n", buf); } fclose(fp); outs(ANSI_COLOR(1;31) "╰┬──────────────────────" "─────────────┬╯" ANSI_RESET "\n"); outs(ANSI_COLOR(1;31;44) "☉┴─────────────────────" "──────────────┴☉" ANSI_RESET "\n"); prints("%55s%s", "留言日期: ", Cdatelite(&_myangel_touched)); move(b_lines - 4, 0); outs("小主人使用上問題找不到小天使請到新手版(" BN_NEWBIE ")\n" " 想留言給小天使請到許\願版(AngelPray)\n" " 想找看板在哪的話可到(AskBoard)\n" "請先在各板上尋找答案或按 Ctrl-P 發問"); // Query if user wants to go to newbie board if (vmsg("請按任意鍵繼續,若想直接進入新手板發文請按 'y'") == 'y') GotoNewHand(); } static void TalkToAngel(){ static char AngelPermChecked = 0; static userinfo_t* lastuent = NULL; userinfo_t *uent; if (strcmp(cuser.myangel, "-") == 0){ NoAngelFound(NULL); return; } if (cuser.myangel[0] && !AngelPermChecked) { userec_t xuser; memset(&xuser, 0, sizeof(xuser)); getuser(cuser.myangel, &xuser); // XXX if user doesn't exist if (!(xuser.userlevel & PERM_ANGEL)) cuser.myangel[0] = 0; } AngelPermChecked = 1; if (cuser.myangel[0] == 0 && !FindAngel()){ lastuent = NULL; NoAngelFound("現在沒有小天使在線上"); return; } // now try to load angel data. // This MUST be done before calling AngelNotOnline, // because it relies on this data. angel_reload_nick(); uent = search_ulist_userid(cuser.myangel); if (uent == NULL || angel_reject_me(uent) || uent->mode == DEBUGSLEEPING){ lastuent = NULL; AngelNotOnline(); return; } // check angelpause: if talked then should accept. if (uent == lastuent) { // we've talked to angel. // XXX what if uentp reused by other? chance very, very low... if (uent->angelpause >= ANGELPAUSE_REJALL) { AngelNotOnline(); return; } } else { if (uent->angelpause) { // lastuent = NULL; AngelNotOnline(); return; } } more("etc/angel_usage", NA); /* 這段話或許可以在小天使回答問題時 show 出來 move(b_lines - 1, 0); outs("現在你的id受到保密,回答你問題的小天使並不知道你是誰 \n" "你可以選擇不向對方透露自己身份來保護自己 "); */ { char xnick[IDLEN+1], prompt[IDLEN*2]; snprintf(xnick, sizeof(xnick), "%s小天使", _myangel_nick); snprintf(prompt, sizeof(prompt), "問%s小天使: ", _myangel_nick); // if success, record uent. if (my_write(uent->pid, prompt, xnick, WATERBALL_ANGEL, uent)) lastuent = uent; } return; } void CallAngel(){ static int entered = 0; screen_backup_t old_screen; if (!HasUserPerm(PERM_LOGINOK) || entered) return; entered = 1; scr_dump(&old_screen); TalkToAngel(); scr_restore(&old_screen); entered = 0; } void pressanykey_or_callangel(){ int ch; if (!HasUserPerm(PERM_LOGINOK)) { pressanykey(); return; } // TODO use visio API instead. outmsg( VCLR_PAUSE_PAD " ▄▄▄▄ " ANSI_COLOR(32) "H " ANSI_COLOR(36) "呼叫小天使" ANSI_COLOR(34) " ▄▄▄▄" ANSI_COLOR(37;44) " 請按 " ANSI_COLOR(36) "空白鍵 " ANSI_COLOR(37) "繼續 " ANSI_COLOR(1;34) "▄▄▄▄▄▄▄▄▄▄▄▄▄▄ " ANSI_RESET); do { ch = igetch(); if (ch == 'h' || ch == 'H'){ CallAngel(); break; } } while ((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n')); move(b_lines, 0); clrtoeol(); refresh(); } #endif // PLAY_ANGEL