配置文件读取
parent
678655ef5d
commit
fbbc6e20af
|
|
@ -1,4 +1,8 @@
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
ConfigReader::ConfigReader()
|
ConfigReader::ConfigReader()
|
||||||
{
|
{
|
||||||
|
|
@ -8,22 +12,78 @@ ConfigReader::~ConfigReader()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigReader::load(std::string& path)
|
std::string ConfigReader::trim(const std::string &s)
|
||||||
{
|
{
|
||||||
|
auto start = s.find_first_not_of(" \t\r\n");
|
||||||
|
auto end = s.find_last_not_of(" \t\r\n");
|
||||||
|
if (start == std::string::npos)
|
||||||
|
return "";
|
||||||
|
return s.substr(start, end - start + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigData::ConfigData()
|
bool ConfigReader::load(std::string &path)
|
||||||
{
|
{
|
||||||
}
|
std::ifstream file(path);
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "无法打开配置文件: " << path << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ConfigData::~ConfigData()
|
std::string line, section;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigData::setSymbol(char wall, char empty, char player, char monster, char trap, char exit)
|
while (std::getline(file, line))
|
||||||
{
|
{
|
||||||
}
|
line = trim(line);
|
||||||
|
|
||||||
void ConfigData::getSymbol(char& wall, char& empty, char& player, char& monster, char& trap, char& exit)
|
if (line.empty() || line[0] == ';' || line[0] == '#')
|
||||||
{
|
continue;
|
||||||
|
|
||||||
|
// 识别 [Section]
|
||||||
|
if (line.front() == '[' && line.back() == ']')
|
||||||
|
{
|
||||||
|
section = line.substr(1, line.size() - 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 Key = Value
|
||||||
|
size_t eq = line.find('=');
|
||||||
|
if (eq == std::string::npos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string key = trim(line.substr(0, eq));
|
||||||
|
std::string value = trim(line.substr(eq + 1));
|
||||||
|
|
||||||
|
// 根据 section 分发
|
||||||
|
if (section == "Symbols")
|
||||||
|
{
|
||||||
|
if (key == "Wall")
|
||||||
|
symbols.setWall(value[0]);
|
||||||
|
else if (key == "Empty")
|
||||||
|
symbols.setEmpty(value[0]);
|
||||||
|
else if (key == "Player")
|
||||||
|
symbols.setPlayer(value[0]);
|
||||||
|
else if (key == "Monster")
|
||||||
|
symbols.setMonster(value[0]);
|
||||||
|
else if (key == "Trap")
|
||||||
|
symbols.setTrap(value[0]);
|
||||||
|
else if (key == "Exit")
|
||||||
|
symbols.setExit(value[0]);
|
||||||
|
}
|
||||||
|
else if (section == "Difficulty")
|
||||||
|
{
|
||||||
|
if (key == "MonsterAI")
|
||||||
|
difficulty = std::stoi(value);
|
||||||
|
}
|
||||||
|
else if (section == "Maze")
|
||||||
|
{
|
||||||
|
if (key == "Width")
|
||||||
|
mazeWidth = std::stoi(value);
|
||||||
|
else if (key == "Height")
|
||||||
|
mazeHeight = std::stoi(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "MazeSymbol.h"
|
#include "MazeData.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class ConfigReader
|
class ConfigReader
|
||||||
|
|
@ -9,29 +9,17 @@ public:
|
||||||
ConfigReader();
|
ConfigReader();
|
||||||
~ConfigReader();
|
~ConfigReader();
|
||||||
|
|
||||||
void load(std::string &path);
|
bool load(const std::string &path);
|
||||||
|
|
||||||
|
MazeData getSymbols() const;
|
||||||
|
void setSymbols(MazeData &value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MazeSymbol symbols; // 存放迷宫符号
|
std::string trim(const std::string &s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MazeData symbols; // 存放迷宫符号
|
||||||
int difficulty = 0; // 怪物AI难度
|
int difficulty = 0; // 怪物AI难度
|
||||||
int mazeWidth = 21;
|
int mazeWidth = 21;
|
||||||
int mazeHeight = 21;
|
int mazeHeight = 21;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ConfigData();
|
|
||||||
~ConfigData();
|
|
||||||
|
|
||||||
void setSymbol(char wall, char empty, char player, char monster, char trap, char exit);
|
|
||||||
void getSymbol(char &wall, char &empty, char &player, char &monster, char &trap, char &exit);
|
|
||||||
|
|
||||||
void setDiffculty(int level);
|
|
||||||
int getDiffculty();
|
|
||||||
|
|
||||||
private:
|
|
||||||
char m_wall, m_empty, m_player, m_monster, m_trap, m_exit;
|
|
||||||
|
|
||||||
int m_monsterAILevel;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,14 @@ Game::~Game()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::loadConfig(const std::string &filePath, MazeData &data)
|
||||||
|
{
|
||||||
|
m_config.setSymbols(data);
|
||||||
|
m_config.load(filePath);
|
||||||
|
}
|
||||||
|
void Game::initMaze()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void Game::run()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,23 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "MazeData.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Game
|
class Game
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ConfigReader m_config;
|
ConfigReader m_config;
|
||||||
ConfigData m_configData;
|
MazeData m_mazeData;
|
||||||
|
|
||||||
bool m_isRunning = true;
|
bool m_isRunning = true;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Game();
|
Game();
|
||||||
~Game();
|
~Game();
|
||||||
|
|
||||||
void loadConfig(const std::string& filePath,ConfigData& data);
|
void loadConfig(const std::string &filePath, MazeData &data);
|
||||||
void initMaze(ConfigData data);
|
void initMaze();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,16 @@ Maze::~Maze()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maze::setMazeSymbols(char wall, char empty, char player, char monster, char trap, char exit)
|
void Maze::setMazeData(MazeData &data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maze::generate(int w, int h, const MazeSymbols &sym)
|
void Maze::generate(int w, int h, const MazeData &sym)
|
||||||
{
|
{
|
||||||
m_symbols = sym;
|
m_symbols = sym;
|
||||||
m_width = (w % 2 == 0) ? w + 1 : w;
|
m_width = (w % 2 == 0) ? w + 1 : w;
|
||||||
m_height = (h % 2 == 0) ? h + 1 : h;
|
m_height = (h % 2 == 0) ? h + 1 : h;
|
||||||
m_grid.assign(m_height, std::string(m_width, m_symbols.wall));
|
m_grid.assign(m_height, std::string(m_width, m_symbols.getWall()));
|
||||||
|
|
||||||
std::vector<std::vector<Cell>> cells(m_height, std::vector<Cell>(m_width));
|
std::vector<std::vector<Cell>> cells(m_height, std::vector<Cell>(m_width));
|
||||||
for (int y = 0; y < m_height; ++y)
|
for (int y = 0; y < m_height; ++y)
|
||||||
|
|
@ -51,7 +51,7 @@ void Maze::generate(int w, int h, const MazeSymbols &sym)
|
||||||
int sx = (startX(gen) / 2) * 2 + 1;
|
int sx = (startX(gen) / 2) * 2 + 1;
|
||||||
int sy = (startY(gen) / 2) * 2 + 1;
|
int sy = (startY(gen) / 2) * 2 + 1;
|
||||||
st.push({sx, sy, true});
|
st.push({sx, sy, true});
|
||||||
m_grid[sy][sx] = m_symbols.empty;
|
m_grid[sy][sx] = m_symbols.getEmpty();
|
||||||
|
|
||||||
// DFS生成
|
// DFS生成
|
||||||
while (!st.empty())
|
while (!st.empty())
|
||||||
|
|
@ -67,8 +67,8 @@ void Maze::generate(int w, int h, const MazeSymbols &sym)
|
||||||
if (inside(nx, ny) && !cells[ny][nx].visited)
|
if (inside(nx, ny) && !cells[ny][nx].visited)
|
||||||
{
|
{
|
||||||
cells[ny][nx].visited = true;
|
cells[ny][nx].visited = true;
|
||||||
m_grid[ny][nx] = m_symbols.empty;
|
m_grid[ny][nx] = m_symbols.getEmpty();
|
||||||
m_grid[c.y + dy / 2][c.x + dx / 2] = m_symbols.empty;
|
m_grid[c.y + dy / 2][c.x + dx / 2] = m_symbols.getEmpty();
|
||||||
st.push({nx, ny, true});
|
st.push({nx, ny, true});
|
||||||
moved = true;
|
moved = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -80,16 +80,16 @@ void Maze::generate(int w, int h, const MazeSymbols &sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 放置出口与陷阱
|
// 放置出口与陷阱
|
||||||
m_grid[1][1] = m_symbols.empty; // 起点
|
m_grid[1][1] = m_symbols.getEmpty(); // 起点
|
||||||
m_grid[m_height - 2][m_width - 2] = m_symbols.exit; // 出口
|
m_grid[m_height - 2][m_width - 2] = m_symbols.getExit(); // 出口
|
||||||
|
|
||||||
// 随机放置陷阱
|
// 随机放置陷阱
|
||||||
std::uniform_int_distribution<> tx(1, m_width - 2), ty(1, m_height - 2);
|
std::uniform_int_distribution<> tx(1, m_width - 2), ty(1, m_height - 2);
|
||||||
for (int i = 0; i < (m_width * m_height) / 80; ++i)
|
for (int i = 0; i < (m_width * m_height) / 80; ++i)
|
||||||
{
|
{
|
||||||
int x = tx(gen), y = ty(gen);
|
int x = tx(gen), y = ty(gen);
|
||||||
if (m_grid[y][x] == m_symbols.empty)
|
if (m_grid[y][x] == m_symbols.getEmpty())
|
||||||
m_grid[y][x] = m_symbols.trap;
|
m_grid[y][x] = m_symbols.getTrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ void Maze::draw(const Position &playerPos, const std::vector<Position> &monsterP
|
||||||
// 绘制玩家
|
// 绘制玩家
|
||||||
if (playerPos.x == x && playerPos.y == y)
|
if (playerPos.x == x && playerPos.y == y)
|
||||||
{
|
{
|
||||||
std::cout << m_symbols.player;
|
std::cout << m_symbols.getPlayer();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ void Maze::draw(const Position &playerPos, const std::vector<Position> &monsterP
|
||||||
{
|
{
|
||||||
if (m.x == x && m.y == y)
|
if (m.x == x && m.y == y)
|
||||||
{
|
{
|
||||||
std::cout << m_symbols.monster;
|
std::cout << m_symbols.getMonster();
|
||||||
drawn = true;
|
drawn = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -132,15 +132,15 @@ bool Maze::isWalkable(const Position &p) const
|
||||||
if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height)
|
if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height)
|
||||||
return false;
|
return false;
|
||||||
char c = m_grid[p.y][p.x];
|
char c = m_grid[p.y][p.x];
|
||||||
return (c == m_symbols.empty || c == m_symbols.trap || c == m_symbols.exit);
|
return (c == m_symbols.getEmpty() || c == m_symbols.getTrap() || c == m_symbols.getExit());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Maze::isExit(const Position &p) const
|
bool Maze::isExit(const Position &p) const
|
||||||
{
|
{
|
||||||
return m_grid[p.y][p.x] == m_symbols.exit;
|
return m_grid[p.y][p.x] == m_symbols.getExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Maze::checkTrap(const Position &p) const
|
bool Maze::checkTrap(const Position &p) const
|
||||||
{
|
{
|
||||||
return m_grid[p.y][p.x] == m_symbols.trap;
|
return m_grid[p.y][p.x] == m_symbols.getTrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,36 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "MazeData.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include<string>
|
|
||||||
|
|
||||||
|
struct Position
|
||||||
struct Position {
|
{
|
||||||
int x, y;
|
int x, y;
|
||||||
bool operator==(const Position& other) const {
|
bool operator==(const Position &other) const
|
||||||
|
{
|
||||||
return x == other.x && y == other.y;
|
return x == other.x && y == other.y;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 用于存储字符符号
|
|
||||||
struct MazeSymbols {
|
|
||||||
char wall, empty, player, monster, trap, exit;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Maze
|
class Maze
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Maze();
|
Maze();
|
||||||
~Maze();
|
~Maze();
|
||||||
|
|
||||||
void setMazeSymbols(char wall, char empty, char player, char monster, char trap, char exit);
|
void setMazeData(MazeData &data);
|
||||||
|
|
||||||
void generate(int w, int h, const MazeSymbols& sym);
|
void generate(int w, int h, const MazeData &sym);
|
||||||
void draw(const Position& playerPos, const std::vector<Position>& monsterPos) const;
|
void draw(const Position &playerPos, const std::vector<Position> &monsterPos) const;
|
||||||
|
|
||||||
bool isWalkable(const Position& p) const;
|
bool isWalkable(const Position &p) const;
|
||||||
bool isExit(const Position& p) const;
|
bool isExit(const Position &p) const;
|
||||||
bool checkTrap(const Position& p) const;
|
bool checkTrap(const Position &p) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
std::vector<std::string> m_grid;
|
std::vector<std::string> m_grid;
|
||||||
MazeSymbols m_symbols;
|
MazeData m_symbols;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "MazeData.h"
|
||||||
|
|
||||||
|
MazeData::MazeData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MazeData::~MazeData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getWall() const
|
||||||
|
{
|
||||||
|
return wall;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setWall(char value)
|
||||||
|
{
|
||||||
|
wall = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getEmpty() const
|
||||||
|
{
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setEmpty(char value)
|
||||||
|
{
|
||||||
|
empty = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getPlayer() const
|
||||||
|
{
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setPlayer(char value)
|
||||||
|
{
|
||||||
|
player = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getMonster() const
|
||||||
|
{
|
||||||
|
return monster;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setMonster(char value)
|
||||||
|
{
|
||||||
|
monster = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getTrap() const
|
||||||
|
{
|
||||||
|
return trap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setTrap(char value)
|
||||||
|
{
|
||||||
|
trap = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char MazeData::getExit() const
|
||||||
|
{
|
||||||
|
return exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MazeData::setExit(char value)
|
||||||
|
{
|
||||||
|
exit = value;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class MazeData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MazeData();
|
||||||
|
~MazeData();
|
||||||
|
|
||||||
|
char getWall() const;
|
||||||
|
|
||||||
|
void setWall(char value);
|
||||||
|
|
||||||
|
char getEmpty() const;
|
||||||
|
|
||||||
|
void setEmpty(char value);
|
||||||
|
|
||||||
|
char getPlayer() const;
|
||||||
|
|
||||||
|
void setPlayer(char value);
|
||||||
|
|
||||||
|
char getMonster() const;
|
||||||
|
|
||||||
|
void setMonster(char value);
|
||||||
|
|
||||||
|
char getTrap() const;
|
||||||
|
|
||||||
|
void setTrap(char value);
|
||||||
|
|
||||||
|
char getExit() const;
|
||||||
|
|
||||||
|
void setExit(char value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char wall;
|
||||||
|
char empty;
|
||||||
|
char player;
|
||||||
|
char monster;
|
||||||
|
char trap;
|
||||||
|
char exit;
|
||||||
|
};
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#include "MazeSymbol.h"
|
|
||||||
|
|
||||||
MazeSymbol::MazeSymbol() {}
|
|
||||||
|
|
||||||
MazeSymbol::~MazeSymbol() {}
|
|
||||||
|
|
||||||
char MazeSymbol::getWall() const
|
|
||||||
{
|
|
||||||
return wall;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setWall(char value)
|
|
||||||
{
|
|
||||||
wall = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
char MazeSymbol::getEmpty() const
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setEmpty(char value)
|
|
||||||
{
|
|
||||||
empty = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
char MazeSymbol::getPlayer() const
|
|
||||||
{
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setPlayer(char value)
|
|
||||||
{
|
|
||||||
player = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
char MazeSymbol::getMonster() const
|
|
||||||
{
|
|
||||||
return monster;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setMonster(char value)
|
|
||||||
{
|
|
||||||
monster = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
char MazeSymbol::getStrap() const
|
|
||||||
{
|
|
||||||
return strap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setStrap(char value)
|
|
||||||
{
|
|
||||||
strap = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
char MazeSymbol::getExit() const
|
|
||||||
{
|
|
||||||
return exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MazeSymbol::setExit(char value)
|
|
||||||
{
|
|
||||||
exit = value;
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
class MazeSymbol
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MazeSymbol();
|
|
||||||
~MazeSymbol();
|
|
||||||
|
|
||||||
char getWall() const;
|
|
||||||
|
|
||||||
void setWall(char value);
|
|
||||||
|
|
||||||
char getEmpty() const;
|
|
||||||
|
|
||||||
void setEmpty(char value);
|
|
||||||
|
|
||||||
char getPlayer() const;
|
|
||||||
|
|
||||||
void setPlayer(char value);
|
|
||||||
|
|
||||||
char getMonster() const;
|
|
||||||
|
|
||||||
void setMonster(char value);
|
|
||||||
|
|
||||||
char getStrap() const;
|
|
||||||
|
|
||||||
void setStrap(char value);
|
|
||||||
|
|
||||||
char getExit() const;
|
|
||||||
|
|
||||||
void setExit(char value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
char wall;
|
|
||||||
char empty;
|
|
||||||
char player;
|
|
||||||
char monster;
|
|
||||||
char strap;
|
|
||||||
char exit;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -9,3 +9,7 @@ Empty =
|
||||||
[Difficulty]
|
[Difficulty]
|
||||||
# 0: 随机移动,1: BFS偏向,2: A*精确追踪
|
# 0: 随机移动,1: BFS偏向,2: A*精确追踪
|
||||||
MonsterAI = 1
|
MonsterAI = 1
|
||||||
|
|
||||||
|
[Maze]
|
||||||
|
Width = 21
|
||||||
|
height = 21
|
||||||
Loading…
Reference in New Issue