import{_ as s,c as i,o as a,a4 as n}from"./chunks/framework.DtvhUNIn.js";const F=JSON.parse('{"title":"6.绘制地图","description":"","frontmatter":{},"headers":[],"relativePath":"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md","filePath":"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md"}'),h={name:"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md"},k=n(`

6.绘制地图

作为一个 RPG 游戏怎么能没有地图呢,是时候绘制地图了!

绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由位置(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。

在虚拟世界中,“道路”可能是连接两个位置的任何东西:一条路,一扇门,沙漠中。基本上,一段经文具有以下属性:

考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同,它作为“可见出口”存在于某个位置(该位置是起点)。它只是与某些命令的行为不同,特别是命令“go”:应用于道路,go将改变玩家的位置。

c
struct object {
   const char    *description;
   const char    *tag;
   struct object *location;
   struct object *destination;
};

注意:

🤔 思考题:为什么创建两个通道可以使我们的程序更加灵活?

接下来我们将展开对象数组

object.h

c
typedef struct object {
   const char    *description;
   const char    *tag;
   struct object *location;
   struct object *destination;
} OBJECT;

extern OBJECT objs[];

#define field      (objs + 0)   //是不是觉得这个很熟悉
#define cave       (objs + 1)
#define silver     (objs + 2)
#define gold       (objs + 3)
#define guard      (objs + 4)
#define player     (objs + 5)
#define intoCave   (objs + 6)
#define exitCave   (objs + 7)

#define endOfObjs  (objs + 8)

object.c

c
#include <stdio.h>
#include "object.h"

OBJECT objs[] = {
   {"an open field"  , "field"   , NULL , NULL  },
   {"a little cave"  , "cave"    , NULL , NULL  },
   {"a silver coin"  , "silver"  , field, NULL  },
   {"a gold coin"    , "gold"    , cave , NULL  },
   {"a burly guard"  , "guard"   , field, NULL  },
   {"yourself"       , "yourself", field, NULL  },
   {"a cave entrance", "entrance", field, cave  },
   {"an exit"        , "exit"    , cave , field }
};

我们将在 misc.c 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。

misc.h

c
extern OBJECT *getPassage(OBJECT *from, OBJECT *to);
extern OBJECT *actorHere(void);
extern int listObjectsAtLocation(OBJECT *location);

misc.c

c
#include <stdio.h>
#include "object.h"

OBJECT *getPassage(OBJECT *from, OBJECT *to)
{
   if (from != NULL && to != NULL)
   {
      OBJECT *obj;
      for (obj = objs; obj < endOfObjs; obj++)   //寻找物品之间是否具有通道
      {
         if (obj->location == from && obj->destination == to)
         {
            return obj;  //找到了
         }
      }
   }
   return NULL;//找不到
}

OBJECT *actorHere(void)
{
   OBJECT *obj;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (obj->location == player->location && obj == guard)
      {
         return obj;
      }
   }
   return NULL;
}

int listObjectsAtLocation(OBJECT *location)
{
   int count = 0;
   OBJECT *obj;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (obj != player && obj->location == location)
      {
         if (count++ == 0)
         {
            printf("You see:\\n");
         }
         printf("%s\\n", obj->description);
      }
   }
   return count;
}

我们将在命令“go”的实现中使用新功能getPassage来确定是否存在可以将玩家带到所需位置的通道。

location.h

c
extern void executeLook(const char *noun);
extern void executeGo(const char *noun);

location.c

c
#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"
#include "noun.h"

void executeLook(const char *noun)
{
   if (noun != NULL && strcmp(noun, "around") == 0)
   {
      printf("You are in %s.\\n", player->location->description);
      listObjectsAtLocation(player->location);
   }
   else
   {
      printf("I don't understand what you want to see.\\n");
   }
}

void executeGo(const char *noun)
{
   OBJECT *obj = getVisible("where you want to go", noun);
   if (obj == NULL)
   {
      // already handled by getVisible
   }
   else if (getPassage(player->location, obj) != NULL)
   //go 只会在有地方的时候才会运行起来
   {
      printf("OK.\\n");
      player->location = obj;
      executeLook("around");
   }
   else if (obj->location != player->location)
   {
      printf("You don't see any %s here.\\n", noun);
   }
   else if (obj->destination != NULL)
   {
      printf("OK.\\n");
      player->location = obj->destination;
      executeLook("around");
   }
   else
   {
      printf("You can't get much closer than this.\\n");
   }
}

我们还将使用新功能getPassage来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。

noun.h

c
extern OBJECT *getVisible(const char *intention, const char *noun);
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);

noun.c

c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"

static bool objectHasTag(OBJECT *obj, const char *noun)
{
   return noun != NULL && *noun != '\\0' && strcmp(noun, obj->tag) == 0;
}

static OBJECT *getObject(const char *noun)
{
   OBJECT *obj, *res = NULL;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (objectHasTag(obj, noun))
      {
         res = obj;
      }
   }
   return res;
}

OBJECT *getVisible(const char *intention, const char *noun)
{
   OBJECT *obj = getObject(noun);
   
   if (obj == NULL)
   {
      printf("I don't understand %s.\\n", intention);
   }
   else if (!(obj == player ||
              obj == player->location ||
              obj->location == player ||
              obj->location == player->location ||
              getPassage(player->location, obj) != NULL ||   //检查两个位置是否相邻
              (obj->location != NULL &&
               (obj->location->location == player ||
                obj->location->location == player->location))))
   {
      printf("You don't see any %s here.\\n", noun);
      obj = NULL;
   }
   return obj;
}

OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
{
   OBJECT *obj = NULL;
   if (from == NULL)
   {
      printf("I don't understand who you want to %s.\\n", verb);
   }
   else if ((obj = getObject(noun)) == NULL)
   {
      printf("I don't understand what you want to %s.\\n", verb);
   }
   else if (obj == from)
   {
      printf("You should not be doing that to %s.\\n", obj->description);
      obj = NULL;
   }
   else if (obj->location != from)
   {
      if (from == player)
      {
         printf("You are not holding any %s.\\n", noun);
      }
      else
      {
         printf("There appears to be no %s you can get from %s.\\n",
                noun, from->description);
      }
      obj = NULL;
   }
   return obj;
}

显然,此示例中的地图是微不足道的:只有两个位置,并且它们在两个方向上都连接在一起。第 12 章将增加第三个地点。

🤔 思考题:

你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道)

注:不用当成任务,自行实验即可

输出样例

Welcome to Little Cave Adventure. You are in an open field. You see: a silver coin a burly guard a cave entrance

--> go entrance OK. You are in a little cave. You see: a gold coin an exit

--> go exit OK. You are in an open field. You see: a silver coin a burly guard a cave entrance

--> go cave OK. You are in a little cave. You see: a gold coin an exit

--> quit

Bye!

`,39),l=[k];function p(t,e,E,r,d,g){return a(),i("div",null,l)}const o=s(h,[["render",p]]);export{F as __pageData,o as default};