ГИС
Теперь рассмотрим catch() — основной метод игровой логики.
На вход поступают координаты игрока и идентификатор целевого покемона. Необходимо определить, поймал ли игрок покемона (напомним, что у каждого покемона есть шанс сбежать).
Сначала полученные данные игрока проверяются на соответствие Avro-схеме. Также проверяется, существует ли такой покемон в базе данных и отображается ли он на карте (покемон должен иметь статус active):
catch = function(self, pokemon_id, player)-- check player datalocal ok, tuple = self.player_model.flatten(player)if not ok thenreturn falseend-- get pokemon datalocal p_tuple = box.space.pokemons:get(pokemon_id)if p_tuple == nil thenreturn falseendlocal ok, pokemon = self.pokemon_model.unflatten(p_tuple)if not ok thenreturn falseendif pokemon.status ~= self.state.ACTIVE thenreturn falseend-- more catch logic to follow<...>end
Далее вычисляется результат: пойман покемон или нет.
Для работы с географическими координатами используется модуль Tarantool gis.
Для простоты конкретная карта не загружается — предполагается, что используется карта мира. Входные координаты также не проверяются: считается, что все полученные местоположения находятся в пределах Земли.
Используются две гео-специфичные переменные:
-
wgs84— последняя редакция стандарта World Geodetic System, WGS84. По сути, это стандартная система координат для Земли, представляющая её в виде эллипсоида. -
nationalmap–- US National Atlas Equal Area. Это спроецированная система координат на основе WGS84. Она предоставляет нулевую базу для проекции местоположения и позволяет позиционировать игроков и покемонов в метрах.
Обе системы зарегистрированы в реестре EPSG Geodhetic Parameter Registry, где каждой системе присвоен уникальный номер. В коде эти номера назначаются соответствующим переменным:
wgs84 = 4326,nationalmap = 2163,
Для игровой логики нужна ещё одна переменная — catch_distance,
которая определяет, на какое расстояние должен подойти игрок к покемону,
чтобы попытаться его поймать. Зададим значение 100 метров.
catch_distance = 100,
Теперь можно вычислить результат. Текущие местоположения игрока
(p_pos) и покемона (m_pos) проецируются на карту, проверяется,
достаточно ли близко находится игрок к покемону (с помощью
catch_distance), и вычисляется, пойман ли покемон (генерируется
случайное значение, и покемон сбегает, если это значение оказывается
меньше 100 минус шанс покемона):
-- project locationslocal m_pos = gis.Point({pokemon.location.x, pokemon.location.y}, self.wgs84):transform(self.nationalmap)local p_pos = gis.Point({player.location.x, player.location.y}, self.wgs84):transform(self.nationalmap)-- check catch distance conditionif p_pos:distance(m_pos) > self.catch_distance thenreturn falseend-- try to catch pokemonlocal caught = math.random(100) >= 100 - pokemon.chanceif caught then-- update and notify on successbox.space.pokemons:update(pokemon_id, {{'=', self.STATUS, self.state.CAUGHT}})self:notify(player, pokemon)endreturn caught