Files
2024-08-10 19:46:55 +08:00

7.6 KiB
Raw Permalink Blame History

8.移动方向

传统的文本冒险使用指南针方向进行导航。

例如,我们在第 6 章中绘制的地图上,玩家可能想向东移动,从田野移动到洞穴。我们可以通过给连接Cave的通道标上“east”来实现这一点。但是我们首先需要解决两个问题。

  1. 我们可能仍然想把这段通道称为“entrance”和“east”。但现在一个对象只能有一个标签。
  2. 在更大的地图上具有更多的位置和道路标签“east”将被定义多次。到目前为止标签在我们的游戏中是全球独一无二的没有重复项。

::: warning 🤔 思考题:你能否想出解决办法? :::

这些问题同样适用于其他对象,而不仅仅是通道。

在我们的冒险中,我们有一枚银币和一枚金币。一方面,如果不接受在只有一枚硬币存在的地点得到硬币,那将是愚蠢的。

另一方面,在两种硬币都在同一位置存在的情况下,玩家应该可以有两个拾取选择。

这立即将我们带到了解析器的第三个问题:

  1. 一个标签只能是一个单词;“sliver coin”这是俩单词他不接受啊

所有三个问题都将在本章中解决,从问题#1 开始。它通过为每个对象提供一个标签列表来解决,而不仅仅是一个标签。

object.h

typedef struct object {
   const char    *description;
   const char   **tags;
   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 wallField  (objs + 8)
#define wallCave   (objs + 9)

#define endOfObjs  (objs + 10)

object.c

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

static const char *tags0[] = {"field", NULL};
static const char *tags1[] = {"cave", NULL};
static const char *tags2[] = {"silver", "coin", "silver coin", NULL};
static const char *tags3[] = {"gold", "coin", "gold coin", NULL};
static const char *tags4[] = {"guard", "burly guard", NULL};
static const char *tags5[] = {"yourself", NULL};
static const char *tags6[] = {"east", "entrance", NULL};
static const char *tags7[] = {"west", "exit", NULL};
static const char *tags8[] = {"west", "north", "south", "forest", NULL};
static const char *tags9[] = {"east", "north", "south", "rock", NULL};
//我们不固定标签长度,在结束的时候用 NULL 来标记
OBJECT objs[] = {
   {"an open field"              , tags0, NULL , NULL  },
   {"a little cave"              , tags1, NULL , NULL  },
   {"a silver coin"              , tags2, field, NULL  },
   {"a gold coin"                , tags3, cave , NULL  },
   {"a burly guard"              , tags4, field, NULL  },
   {"yourself"                   , tags5, field, NULL  },
   {"a cave entrance to the east", tags6, field, cave  },
   {"an exit to the west"        , tags7, cave , field },
   {"dense forest all around"    , tags8, field, NULL  },
   {"solid rock all around"      , tags9, cave , NULL  }
   //我们用 NULL 来阻绝进入一个你不知道的地方
};

当然,要让这个改动生效,我们还需要调整noun.c中的objectHasTag函数。

同时,我们将让函数 getVisiblegetPossession 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。

noun.h

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

noun.c

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

static bool objectHasTag(OBJECT *obj, const char *noun)
{
   if (noun != NULL && *noun != '\0')
   {
      const char **tag;
      for (tag = obj->tags; *tag != NULL; tag++)
      {
         if (strcmp(*tag, noun) == 0) return true;
      }//扫描对象的 tag 列表
   }
   return false;
}

static OBJECT ambiguousNoun;//我们需要这玩意的地址帮助我们把它当成一个返回值

static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance)
{
   OBJECT *obj, *res = NULL;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance)
      {
         res = res == NULL ? obj : &ambiguousNoun;//标签不明确的解决方案哦
      }
   }
   return res;
}

OBJECT *getVisible(const char *intention, const char *noun)
{
   OBJECT *obj = getObject(noun, player, distOverthere);
   if (obj == NULL)
   {
      if (getObject(noun, player, distNotHere) == NULL)
      {
         printf("I don't understand %s.\n", intention);
      }
      else
      {
         printf("You don't see any %s here.\n", noun);
      }
   }
   else if (obj == &ambiguousNoun)//模糊匹配
   {
      printf("Please be specific about which %s you mean.\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, from, distHeldContained)) == NULL)
   {
      if (getObject(noun, player, distNotHere) == NULL)
      {
         printf("I don't understand what you want to %s.\n", verb);
      }
      else 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);
      }
   }
   else if (obj == &ambiguousNoun)//模糊匹配
   {
      printf("Please be specific about which %s you want to %s.\n",
             noun, verb);
      obj = NULL;
   }
   else if (obj == from)
   {
      printf("You should not be doing that to %s.\n", obj->description);
      obj = NULL;
   }
   return obj;
}

问题 #3 可以通过从函数parseAndExecute中删除一个 空格字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。

parsexec.c

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "location.h"
#include "inventory.h"

bool parseAndExecute(char *input)
{
   char *verb = strtok(input, " \n");
   char *noun = strtok(NULL, "\n");
   if (verb != NULL)
   {
      if (strcmp(verb, "quit") == 0)
      {
         return false;
      }
      else if (strcmp(verb, "look") == 0)
      {
         executeLook(noun);
      }
      else if (strcmp(verb, "go") == 0)
      {
         executeGo(noun);
      }
      else if (strcmp(verb, "get") == 0)
      {
         executeGet(noun);
      }
      else if (strcmp(verb, "drop") == 0)
      {
         executeDrop(noun);
      }
      else if (strcmp(verb, "give") == 0)
      {
         executeGive(noun);
      }
      else if (strcmp(verb, "ask") == 0)
      {
         executeAsk(noun);
      }
      else if (strcmp(verb, "inventory") == 0)
      {
         executeInventory();
      }
      else
      {
         printf("I don't know how to '%s'.\n", verb);
      }
   }
   return true;
}

模块main.cinventory.clocation.cmove.cmisc.c保持不变

现在对象数组 ( object.c ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。

::: warning 🤔 猜猜看该怎么办? :::