Peoplemon  0.1.0
Peoplemon 3 game source documentation
Conversation.cpp
Go to the documentation of this file.
2 
3 #include <Core/Items/Item.hpp>
4 #include <Core/Properties.hpp>
5 #include <Core/Resources.hpp>
6 
7 namespace core
8 {
9 namespace file
10 {
11 namespace
12 {
13 constexpr unsigned int EndNode = 9999999;
14 
15 void shiftDownJumps(std::vector<Conversation::Node>& nodes, unsigned int i) {
16  const auto updateJump = [&nodes, i](std::uint32_t& jump) {
17  const auto isSimpleNext = [&nodes](unsigned int i) {
18  if (i >= nodes.size()) return false;
19  switch (nodes[i].getType()) {
25  return true;
26 
27  default:
28  return false;
29  }
30  };
31 
32  if (jump == i) {
33  if (isSimpleNext(jump)) {
34  const std::uint32_t j = nodes[jump].next();
35  jump = j > i ? j - 1 : j;
36  }
37  else { jump = EndNode; }
38  }
39  else if (jump > i)
40  --jump;
41  };
42 
43  for (unsigned int j = 0; j < nodes.size(); ++j) {
44  Conversation::Node& node = nodes[j];
45 
46  // Invalidate or shift down
47  updateJump(node.nextOnPass());
48  updateJump(node.nextOnReject());
49 
50  // Choices
51  if (node.getType() == Conversation::Node::Prompt) {
52  for (auto& pair : node.choices()) {
53  // Invalidate or shift down
54  updateJump(pair.second);
55  }
56  }
57  }
58 }
59 } // namespace
60 
61 bool Conversation::load(const std::string& file) {
62  return ConversationManager::initializeExisting(file, *this);
63 }
64 
65 bool Conversation::loadDev(std::istream& input) {
66  return bl::serial::json::Serializer<Conversation>::deserializeStream(input, *this);
67 }
68 
69 bool Conversation::loadProd(bl::serial::binary::InputStream& input) {
70  return bl::serial::binary::Serializer<Conversation>::deserialize(input, *this);
71 }
72 
73 bool Conversation::save(const std::string& file) const {
74  std::ofstream output(file.c_str());
75  return bl::serial::json::Serializer<Conversation>::serializeStream(output, *this, 4, 0);
76 }
77 
78 bool Conversation::saveBundle(bl::serial::binary::OutputStream& output,
79  bl::resource::bundle::FileHandlerContext&) const {
80  return bl::serial::binary::Serializer<Conversation>::serialize(output, *this);
81 }
82 
83 const std::vector<Conversation::Node>& Conversation::nodes() const { return cnodes; }
84 
85 void Conversation::deleteNode(unsigned int i) {
86  if (i < cnodes.size()) {
87  shiftDownJumps(cnodes, i);
88  cnodes.erase(cnodes.begin() + i);
89  }
90  else { BL_LOG_WARN << "Tried to delete out of range node: " << i; }
91 }
92 
93 void Conversation::appendNode(const Node& node) { cnodes.push_back(node); }
94 
95 void Conversation::setNode(unsigned int i, const Node& node) {
96  if (i < cnodes.size()) { cnodes[i] = node; }
97  else { BL_LOG_WARN << "Out of range node assign: " << i; }
98 }
99 
101 
102 Conversation::Node::Node(Type t) { setType(t); }
103 
105  type = t;
106  prompt.clear();
107  next() = nextOnPass() = nextOnReject() = 999999;
108 
109  switch (type) {
110  case GiveItem:
111  case TakeItem:
112  data.emplace<Item>();
113  break;
114 
115  case GiveMoney:
116  case TakeMoney:
117  data.emplace<std::uint32_t>(0);
118  break;
119 
120  case Prompt:
121  data.emplace<std::vector<std::pair<std::string, std::uint32_t>>>();
122  break;
123 
124  case RunScript:
125  data.emplace<bool>(false);
126  break;
127 
128  default:
129  break;
130  }
131 }
132 
134 
135 std::string& Conversation::Node::message() { return prompt; }
136 
137 std::string& Conversation::Node::script() { return prompt; }
138 
139 std::string& Conversation::Node::saveFlag() { return prompt; }
140 
142  static bool null = false;
143  bool* r = std::get_if<bool>(&data);
144  if (r) return *r;
145  BL_LOG_ERROR << "Bad Node access (concurrent). Type=" << static_cast<int>(type);
146  return null;
147 }
148 
150  const bool* r = std::get_if<bool>(&data);
151  if (r) return *r;
152  BL_LOG_ERROR << "Bad Node access (concurrent). Type=" << static_cast<int>(type);
153  return false;
154 }
155 
156 std::vector<std::pair<std::string, std::uint32_t>>& Conversation::Node::choices() {
157  static std::vector<std::pair<std::string, std::uint32_t>> null;
158  auto* d = std::get_if<std::vector<std::pair<std::string, std::uint32_t>>>(&data);
159  if (d) { return *d; }
160  BL_LOG_ERROR << "Bad Node access (choices). Type=" << static_cast<int>(type);
161  return null;
162 }
163 
164 std::uint32_t& Conversation::Node::money() {
165  static std::uint32_t null;
166  std::uint32_t* m = std::get_if<std::uint32_t>(&data);
167  if (m) return *m;
168  BL_LOG_ERROR << "Bad node access (money). Type=" << static_cast<int>(type);
169  return null;
170 }
171 
173  static Item null;
174  Item* i = std::get_if<Item>(&data);
175  if (i) return *i;
176  BL_LOG_ERROR << "Bad node access (item). Type=" << static_cast<int>(type);
177  return null;
178 }
179 
180 std::uint32_t& Conversation::Node::next() { return jumps[0]; }
181 
182 std::uint32_t& Conversation::Node::nextOnPass() { return jumps[0]; }
183 
184 std::uint32_t& Conversation::Node::nextOnReject() { return jumps[1]; }
185 
186 const std::string& Conversation::Node::message() const { return prompt; }
187 
188 const std::string& Conversation::Node::script() const { return prompt; }
189 
190 const std::string& Conversation::Node::saveFlag() const { return prompt; }
191 
192 const std::vector<std::pair<std::string, std::uint32_t>>& Conversation::Node::choices() const {
193  static std::vector<std::pair<std::string, std::uint32_t>> null;
194  const auto* d = std::get_if<std::vector<std::pair<std::string, std::uint32_t>>>(&data);
195  if (d) { return *d; }
196  BL_LOG_ERROR << "Bad Node access (choices). Type=" << type;
197  return null;
198 }
199 
200 unsigned int Conversation::Node::money() const {
201  static unsigned int null;
202  const unsigned int* m = std::get_if<unsigned int>(&data);
203  if (m) return *m;
204  BL_LOG_ERROR << "Bad node access (money). Type=" << static_cast<int>(type);
205  return null;
206 }
207 
209  static const Item null;
210  const Item* i = std::get_if<Item>(&data);
211  if (i) return *i;
212  BL_LOG_ERROR << "Bad node access (item). Type=" << static_cast<int>(type);
213  return null;
214 }
215 
216 std::uint32_t Conversation::Node::next() const { return jumps[0]; }
217 
218 std::uint32_t Conversation::Node::nextOnPass() const { return jumps[0]; }
219 
220 std::uint32_t Conversation::Node::nextOnReject() const { return jumps[1]; }
221 
223  Conversation conv;
224 #ifdef PEOPLEMON_DEBUG
225  Node node;
226  node.setType(Node::Talk);
227  node.message() = "WARNING: Failed to load conversation: " + f;
228  node.next() = 1;
229  conv.appendNode(node);
230 #else
231  (void)f;
232 #endif
233  return conv;
234 }
235 
237  switch (t) {
238  case Node::Type::Talk:
239  return "Talk";
240  case Node::Type::Prompt:
241  return "Question";
242  case Node::Type::GiveItem:
243  return "Give Item";
244  case Node::Type::TakeItem:
245  return "Take Item";
246  case Node::Type::GiveMoney:
247  return "Give Money";
248  case Node::Type::TakeMoney:
249  return "Take Money";
250  case Node::Type::RunScript:
251  return "Run Script";
252  case Node::Type::CheckSaveFlag:
253  return "Check Flag";
254  case Node::Type::SetSaveFlag:
255  return "Set Flag";
256  case Node::Type::CheckInteracted:
257  return "Check Talked";
258  default:
259  return "Unknown";
260  }
261 }
262 
263 void Conversation::getNextJumps(const Node& node, std::vector<unsigned int>& next) {
264  using T = Node::Type;
265 
266  next.clear();
267  switch (node.getType()) {
268  case T::Talk:
269  case T::RunScript:
270  case T::SetSaveFlag:
271  case T::GiveMoney:
272  case T::GiveItem:
273  next.push_back(node.next());
274  break;
275  case T::CheckInteracted:
276  case T::CheckSaveFlag:
277  case T::TakeMoney:
278  case T::TakeItem:
279  next.push_back(node.nextOnPass());
280  next.push_back(node.nextOnReject());
281  break;
282  case T::Prompt:
283  for (const auto& c : node.choices()) { next.push_back(c.second); }
284  break;
285  default:
286  BL_LOG_ERROR << "Unknown node type: " << node.getType();
287  break;
288  }
289 }
290 
291 } // namespace file
292 } // namespace core
Core classes and functionality for both the editor and game.
Stores a conversation that an NPC or trainer can have with the player.
bool saveBundle(bl::serial::binary::OutputStream &output, bl::resource::bundle::FileHandlerContext &ctx) const
Saves the data from this object to the given bundle and registers depency files if any.
static void getNextJumps(const Node &node, std::vector< unsigned int > &jumps)
Populates the jumps vector with the indices reachable from the given node.
bool load(const std::string &file)
Loads the conversation from the given file.
void deleteNode(unsigned int i)
Deletes the node at the given index. Jump indexes for other nodes are updated if affected....
void setNode(unsigned int i, const Node &node)
Updates the value of the node at the given index.
bool loadProd(bl::serial::binary::InputStream &input)
Loads the conversation from its binary format.
const std::vector< Node > & nodes() const
Returns the list of nodes in the conversation.
bool loadDev(std::istream &input)
Loads the conversation from its json format.
bool save(const std::string &file) const
Saves the conversation to the given file.
static Conversation makeLoadError(const std::string &filename)
Helper function to create a conversation that reports an error if in debug mode.
void appendNode(const Node &node)
Appends the given node to the list of nodes.
Building block of conversations. A conversation is a tree of nodes and each node is an action that ha...
std::string & script()
Returns the script file of this node.
static std::string typeToString(Type type)
Converts a node type to a human readable string.
std::string & message()
Returns the message or prompt for this node.
std::uint32_t & nextOnPass()
Returns the next node if a check passes (ie take item or money)
std::uint32_t & nextOnReject()
Returns the next node if a check is rejected (ie not taking money or item)
void setType(Type type)
Clears node data and updates to a new type.
std::uint32_t & money()
Returns the money requested or given, undefined behavior if not a money node.
Type getType() const
Returns the type of this node.
bool & runConcurrently()
Returns whether or not the launched script should be executed concurrently.
std::string & saveFlag()
Returns the save flag of this node.
Item & item()
Returns the item to give or take. Undefined behavior if not an item node.
std::uint32_t & next()
Returns the index of the next node in the case of a Talk, Give, and Script nodes.
std::vector< std::pair< std::string, std::uint32_t > > & choices()
Returns the choices if this is a prompt node. Undefined behavior if not.
Node()
Creates an empty talk node.
Type
Represents the different effects that nodes can have.
@ RunScript
This will run a script. Must be a file.
@ Prompt
This will output a message then prompt the player for a choice.
@ Talk
This just outputs a message.
@ GiveItem
This unconditionally gives the player an item.
@ SetSaveFlag
This will check if a flag exists and jump to the next node accordingly.
@ GiveMoney
This will unconditionally give money to the player.