回精華區
作者itoc, 信區: itoc
標題[遊戲] rich.c 大富翁
時間03/12/07
作者: itoc (League) 看板: plan
標題: [功能] 大富翁
時間: 2003/12/03 Wed 17:49:04                           Updated: 2003/12/22

  這個大富翁雖然地圖是一樣的
  不過其他的地價及機會命運都是我亂編的 :p

: config.h

#define PIPSHM_KEY      4998    /* 電子雞對戰 */
+ #define RICHSHM_KEY   4997    /* 大富翁 */

: menu.c 適當選單加入

+  "bin/rich.so:main_rich", PERM_BASIC, - M_GAME,
+  "Rich       大富翁",

: game/Makefile

SO =     ..... rich.so

: etc/game/rich.land 新增這檔案
: 第一欄是路名
: 第二欄 >10 中一樣的數字表示同一區的,踩到要多收錢
: 第三欄是價格

由此去      -1  0
建國南路    11  500
命運        0   0
建國北路    11  600
付所得稅    -2  0
臺北車站    1   1000
健康路      12  800
機會        0   0
光明路      12  800
五福路      12  1000
坐牢路過    -1  0
自由路      13  1100
電力公司    2   1500
平等路      13  1100
博愛路      13  1200
臺中車站    1   1000
延平三路    14  1400
命運        0   0
延平二路    14  1400
延平一路    14  1500
停車場      -1  0
忠孝路      15  1800
機會        0   0
仁愛路      15  1800
信義路      15  2000
臺南車站    1   1000
民族路      16  2200
民權路      16  2200
自來水廠    2   1500
民生路      16  2500
直接進牢    -3  0
中正路      17  3000
中華路      17  3000
命運        0   0
介壽路      17  3200
高雄車站    1   1000
機會        0   0
新生南路    18  3500
付所得稅    -2  0
新生北路    18  4000

: src/game/rich.c 新增這檔案

/*-------------------------------------------------------*/
/* rich.c       ( NTHU CS MapleBBS Ver 3.10 )            */
/*-------------------------------------------------------*/
/* target : 大富翁                                       */
/* create : 02/07/01                                     */
/* update : 03/07/14                                     */
/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw                 */
/*-------------------------------------------------------*/


#include "bbs.h"

#ifdef HAVE_GAME

#define MAX_RICHUSER    10  /* 使用者人數上限,最大可設到 26
                               (否則英文字母會不夠用) */
#define MAX_LAND        40  /* 全部有 40 塊地 */


typedef struct
{
  char userid[IDLEN + 1];   /* ID */
  int lose;                 /* 1: 輸了 */
  int money;                /* 錢 */
  int pos;                  /* 位置 (0~MAX_LAND-1 分別代表 由此去~新生北路) */
}   RICH_PLAYER;


typedef struct
{
  char name[9];             /* 土地名 */
  int owner;                /* 土地擁有者 */
  int district;             /* >0:地段編號(可購買) >10:一般建地
                               1:車站 2:電廠水廠 0:機會命運
                               -1:路過 -2:付稅 -3:坐牢 */
  int cost;                 /* 土地價值 */
  int floor;                /* 樓層 */
}   RICH_LAND;


typedef struct
{
  int numuser;              /* 本桌目前有幾人 */
  int maxuser;              /* 本桌最多容納幾人 */
  int nowturn;              /* 現在輪到誰 */
  int numlose;              /* 有幾個人倒閉了 */

  RICH_PLAYER player[MAX_RICHUSER];

  RICH_LAND land[MAX_LAND]; /* 0~39 這些地的狀態 */
}   RICH_STRUCT;


static RICH_STRUCT *ri;
static int mynum;                   /* 我是第幾個玩家 */
static RICH_PLAYER *myrp;


/*-------------------------------------------------------*/
/* 計算罰金                                              */
/*-------------------------------------------------------*/


static int
count_pay(rl)
  RICH_LAND *rl;
{
  int i, pay;
  RICH_LAND *rlp;

  pay = 0;
  for (i = 0; i < MAX_LAND; i++)
  {
    rlp = &ri->land[i];

    /* 同區段同主人,罰金要加錢 */
    if (rlp->district == rl->district && rlp->owner == rl->owner)
      pay += rlp->cost * rlp->floor / 20;
  }

  return pay;
}


/*-------------------------------------------------------*/
/* 玩家函式                                              */
/*-------------------------------------------------------*/


static void
rp_init()
{
  ri->numuser++;

  /* 每個人一開始的狀態 */
  myrp = &ri->player[mynum];
  strcpy(myrp->userid, cuser.userid);
  myrp->lose = 0;
  myrp->money = 10000;
  myrp->pos = 0;
}


static void
rp_exit()
{
  ri->numuser--;

  /* 恢復初始狀態 */
  myrp->userid[0] ='\0';
}


static int  /* 0:繼續玩 -1:輸了*/
rp_play()
{
  int pay;
  char msg[80];
  RICH_PLAYER *rp;
  RICH_LAND *rl;

  myrp->pos += rnd(6) + 1;  /* 每次走 1~6 格 */
  myrp->pos %= MAX_LAND;

  rl = &ri->land[myrp->pos];

  if (rl->owner >= 0)
  {
    if (rl->owner != mynum) /* 別人的地盤 */
    {
      rp = &ri->player[rl->owner];
      pay = count_pay(rl);
      rp->money += pay;
      myrp->money -= pay;
      sprintf(msg, "您來到了[%s],這是 %s 的地盤,得付他 %d 元",
        rl->name, rp->userid, pay);
      vmsg(msg);
    }
    else            /* 我的地盤 */
    {
      if (rl->district > 10)
      {
        if (myrp->money >= rl->cost)
        {
          if (rl->floor < 5)
          {
            sprintf(msg, "您來到了[%s],這是自已的地,要投資建設嗎(Y/N)?[N] ",
              rl->name);
            if (vans(msg) == 'y')
            {
              myrp->money -= rl->cost;
              rl->floor++;
              vmsg("您建設了這塊地");
            }
            else
            {
              vmsg("您放棄建設這塊地");
            }
          }
          else
          {
            sprintf(msg, "您來到了[%s],這塊地已經五層樓高了", rl->name);
            vmsg(msg);
          }
        }
        else
        {
          sprintf(msg, "嗚嗚,[%s]是自己的地,可是您沒錢建設", rl->name);
          vmsg(msg);
        }
      }
      else
      {
        sprintf(msg, "您來到了自己的地盤[%s]", rl->name);
        vmsg(msg);
      }
    }
  }
  else
  {
    if (rl->district > 0)       /* 可購買的地段 */
    {
      if (myrp->money >= rl->cost)
      {
        sprintf(msg, "您來到了[%s],這是沒有人的地,要買下來嗎(Y/N)?[N] ",
          rl->name);
        if (vans(msg) == 'y')
        {
          myrp->money -= rl->cost;
          rl->owner = mynum;
          vmsg("您買下了這塊地");
        }
        else
        {
          vmsg("您放棄購買這塊地");
        }
      }
      else
      {
        sprintf(msg, "嗚嗚,[%s]是沒有人的地,可是您沒錢買", rl->name);
        vmsg(msg);
      }
    }
    else if (rl->district == -1)    /* 路過 */
    {
      sprintf(msg, "您來到了[%s],什麼事都沒發生", rl->name);
      vmsg(msg);
    }
    else if (rl->district == -2)    /* 扣稅 */
    {
      myrp->money = myrp->money >> 1;
      sprintf(msg, "您來到了[%s],身上的錢被站長搶了一半", rl->name);
      vmsg(msg);
    }
    else if (rl->district == -3)    /* 坐牢 */
    {
      sprintf(msg, "您來到了[%s],被記三支大過勒令退學,輸了", rl->name);
      vmsg(msg);
      return -1;
    }
    else                            /* 機會命運 */
    {
      sprintf(msg, "您來到了[%s],試試自己的手氣", rl->name);
      vmsg(msg);

      /* 隨便寫的 :p */
      switch (rnd(3))
      {
      case 0:
        myrp->money += 100;
        vmsg("考試考 100 分,得到獎學金 100 元 (學校很窮)");
        break;

      case 1:
        myrp->money -= 1000;
        vmsg("走路踩到香蕉皮跌倒,付醫藥費 1000 元 (醫院很黑)");
        break;

      case 2:
        myrp->money = 0;
        vmsg("遇到強盜,把身上的錢都搶光了");
        break;
      }
    }
  }

  if (myrp->money < 0)
  {
    ri->numlose++;
    myrp->lose = 1;
    vmsg("哇哇,您沒錢了宣告倒閉,輸了");
    return -1;
  }

  return 0;
}


/*-------------------------------------------------------*/
/* 土地函式                                              */
/*-------------------------------------------------------*/


static void
rl_init()
{
  int i;
  char district[10], cost[10];
  FILE *fp;
  RICH_LAND *rl;

  if (!(fp = fopen("etc/game/rich.land", "r")))
    return;

  for (i = 0; i < MAX_LAND; i++)
  {
    rl = &ri->land[i];
    fscanf(fp, "%s %s %s", rl->name, district, cost);
    rl->owner = -1;
    rl->district = atoi(district);
    rl->cost = atoi(cost);
    rl->floor = 1;
  }

  fclose(fp);
}


static void
rl_clear()      /* 將自己的土地歸公有 */
{
  int i;
  RICH_LAND *rl;

  for (i = 0; i < MAX_LAND; i++)
  {
    rl = &ri->land[i];
    if (rl->owner == mynum)
    {
      rl->owner = -1;
      rl->floor = 1;
    }
  }
}


/*-------------------------------------------------------*/
/* 大富翁函式                                            */
/*-------------------------------------------------------*/


static int      /* -1:失敗  >=0:我是第幾個玩家 */
rich_initshm()
{
  int num, max;
  char ans[5], buf[80];

  ri = shm_new(RICHSHM_KEY, sizeof(*ri));
  if (shmget(RICHSHM_KEY, sizeof(*ri), 0) < 0)
    ri->numuser = 0;

  num = ri->numuser;
  if (num > MAX_RICHUSER)
  {
    vmsg("抱歉,桌長設定中,請稍候");
    return -1;
  }

  if (!num) /* 第一個進入的人是桌長 */
  {
    memset(ri, 0, sizeof(RICH_STRUCT));

    ri->numuser = MAX_RICHUSER + 1; /* 桌長設定中 */
    sprintf(buf, "幾個人要玩(2~%d)? ", MAX_RICHUSER);
    vget(b_lines, 0, buf, ans, 3, DOECHO);
    max = atoi(ans);
    ri->numuser = 0;            /* 桌長設定完成 */
    if (max < 2 || max > MAX_RICHUSER)
      return -1;
    ri->maxuser = max;
  }
  else      /* 桌內已經有人,找一個空位 */
  {
    max = ri->maxuser;
    for (num = 1; num < max; num++) /* 第 0 個位子是桌長 */
    {
      if (!ri->player[num].userid[0])
        break;
    }
    if (num == max) /* 找不到空位 */
    {
      vmsg("抱歉,桌內已滿,下次請早");

#if 1   /* 如果確定出錯了,站長可以清桌 */
      if (HAS_PERM(PERM_SYSOP) && vans("站長要清桌踢人(Y/N)?[N] ") =='y')
        ri->numuser = 0;
#endif

      return -1;
    }
  }

  mynum = num;
  rp_init();

  rl_init();
}


static int      /* 0:已滿桌  -1:離開 */
rich_wait()     /* 等待滿桌 */
{
  int num, max, fd;
  struct timeval tv = {1, 0};

  num = -1;
  max = ri->maxuser;
  do            /* 查人數是否足夠 */
  {
    if (num != ri->numuser) /* 有變動才 refresh */
    {
      move(b_lines, 0);
      clrtoeol();
      prints("目前有 %d/%d 個玩家,等待其他的使用者加入,按 q 離開",
        ri->numuser, max);
      refresh();
      num = ri->numuser;
    }

    fd = 1;
    if (select(1, (fd_set *) &fd, NULL, NULL, &tv) > 0)
    {
      if (vkey() == 'q')
      {
        rp_exit();
        return -1;
      }
    }
  } while (num < max);

  return 0;
}


static void
rich_display()
{
  clear();
  move(0, 0);
  outs("    免費  │忠孝│機會│仁愛│信義│臺南│民族│民權│自來│民生│ 直  接 \n");
  outs("   停車場 │ 路 │    │ 路 │ 路 │車站│ 路 │ 路 │水廠│ 路 │ 進  牢 \n");
  outs("  ────┼──┴──┴──┴──┴──┴──┴──┴──┴──┼────\n");
  outs("  延平一路│                                                    │中 正 路\n");
  outs("  ────┤                                                    ├────\n");
  outs("  延平二路│                                                    │中 華 路\n");
  outs("  ────┤                                                    ├────\n");
  outs("  命    運│                                                    │命    運\n");
  outs("  ────┤                                                    ├────\n");
  outs("  延平三路│                                                    │介 壽 路\n");
  outs("  ────┤                                                    ├────\n");
  outs("  臺中車站│  ─┤↑命令區├──┤            ├──┤  ├──  │高雄車站\n");
  outs("  ────┤                                                    ├────\n");
  outs("  博 愛 路│                                                    │機    會\n");
  outs("  ────┤                                                    ├────\n");
  outs("  平 等 路│                                                    │新生南路\n");
  outs("  ────┤                                                    ├────\n");
  outs("  電力公司│                                                    │付所得稅\n");
  outs("  ────┤  →                                                ├────\n");
  outs("  自 由 路│                                                    │新生北路\n");
  outs("  ────┼──┬──┬──┬──┬──┬──┬──┬──┬──┼────\n");
  outs("   坐  牢 │五福│光明│機會│健康│臺北│付所│建國│命運│建國│由 此 去\n");
  outs("   路  過 │ 路 │ 路 │    │ 路 │車站│得稅│北路│    │南路│        \n");
}


static void
rich_play()
{
  int nowturn, nexturn, wait, fd;
  struct timeval tv = {1, 0};

  /* 開始繞圈圈了 */

  srand(time(NULL) - cuser.userno); /* 讓所有人的 seed 都不同 */

  for (;;)
  {
    /* 輪到別人 */
    wait = 0;
    nowturn = -1;
    while (mynum != ri->nowturn)
    {
      if (nowturn != ri->nowturn)   /* 有變動才 refresh */
      {
        move(b_lines, 0);
        clrtoeol();
        nowturn = ri->nowturn;
        prints("目前輪到 %c. %s", 'A' + nowturn, ri->player[nowturn].userid);
        refresh();
      }

      fd = 1;
      select(1, (fd_set *) &fd, NULL, NULL, &tv);

      if (++wait > 300)
      {
        ri->numuser = 0;
        vmsg("有人太久未完成行動,應該是斷線了,遊戲停止進行");
        return;
      }
    }

    /* 輪到自己 */
    if (ri->numlose == ri->numuser - 1)
    {
      vmsg("哇哇,其他人都倒閉了,您獲勝了");
      ri->numuser = 0;
      return;
    }
    outz("請按任意鍵擲骰子\033[5m...\033[m");
    vkey();

    if ((fd = rp_play()) < 0)
      rl_clear();

    /* 換下一個還沒輸的人 */
    for (nexturn = ri->nowturn;;)
    {
      nexturn = (nexturn == ri->maxuser - 1) ? 0 : nexturn + 1;
      if (!((&ri->player[nexturn])->lose))
        break;
    }
    ri->nowturn = nexturn;

    if (fd < 0)     /* 我輸了 */
      return;
  }
}


/*-------------------------------------------------------*/
/* 主程式                        */
/*-------------------------------------------------------*/


int
main_rich()
{
  if (cutmp->status & STATUS_COINLOCK)
  {
    vmsg(MSG_COINLOCK);
    return XEASY;
  }

  if (rich_initshm() < 0)
    return XEASY;

  if (rich_wait() < 0)
    return XEASY;

  rich_display();
  rich_play();

  return 0;
}
#endif

--
□ 本文章由 itocpc512-2.EE.NCTU.edu.tw 發表
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻