1 /*
2  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 #include "WorldBranch.h"
10 
11 #include "def-script.h"
12 #include "Log.h"
13 #include "Path.h"
14 #include "LevelNode.h"
15 #include "LevelStatus.h"
16 #include "ScriptState.h"
17 #include "ResDialogPack.h"
18 #include "LevelDesc.h"
19 #include "LogicException.h"
20 
21 #include "worldmap-script.h"
22 
23 //-----------------------------------------------------------------
WorldBranch(LevelNode * root)24 WorldBranch::WorldBranch(LevelNode *root)
25 {
26     m_root = root;
27     m_ending = NULL;
28     m_outPack = NULL;
29 
30     m_script->registerFunc("worldmap_addDesc", script_worldmap_addDesc);
31     m_script->registerFunc("branch_addNode", script_branch_addNode);
32     m_script->registerFunc("branch_setEnding", script_branch_setEnding);
33     m_script->registerFunc("node_bestSolution", script_node_bestSolution);
34 }
35 //-----------------------------------------------------------------
36 /**
37  * Execute script which will add nodes.
38  * @param datafile worldmap file
39  * @param outEnding pointer to store ending node. It is not changed when
40  * endingNode is set.
41  * @param destPack pack to store node descriptions
42  * @return root node (can be NULL)
43  */
44 LevelNode *
parseMap(const Path & datafile,LevelNode ** outEnding,ResDialogPack * destPack)45 WorldBranch::parseMap(const Path &datafile, LevelNode **outEnding,
46         ResDialogPack *destPack)
47 {
48     m_outPack = destPack;
49     scriptInclude(datafile);
50     m_outPack = NULL;
51 
52     if (m_ending) {
53         if (outEnding) {
54             *outEnding = m_ending;
55         }
56         else {
57             throw LogicException(ExInfo("cannot export ending node")
58                     .addInfo("ending", m_ending->getCodename()));
59         }
60     }
61 
62     if (m_root && m_root->getState() < LevelNode::STATE_OPEN) {
63         m_root->setState(LevelNode::STATE_OPEN);
64     }
65     return m_root;
66 }
67 //-----------------------------------------------------------------
68     void
addDesc(const std::string & codename,LevelDesc * desc)69 WorldBranch::addDesc(const std::string &codename, LevelDesc *desc)
70 {
71     if (m_outPack) {
72         m_outPack->addRes(codename, desc);
73     }
74     else {
75         throw LogicException(ExInfo("cannot export level description")
76                 .addInfo("codename", codename));
77     }
78 }
79 //-----------------------------------------------------------------
80 /**
81  * Add new node to branch.
82  */
83 void
addNode(const std::string & parent,LevelNode * new_node,bool hidden)84 WorldBranch::addNode(const std::string &parent, LevelNode *new_node,
85         bool hidden)
86 {
87     prepareNode(new_node, hidden);
88     insertNode(parent, new_node);
89 }
90 //-----------------------------------------------------------------
91 /**
92  * Take ending node.
93  */
94 void
setEnding(LevelNode * new_node)95 WorldBranch::setEnding(LevelNode *new_node)
96 {
97     if (m_ending) {
98         delete m_ending;
99     }
100     m_ending = new_node;
101     if (wasSolved(m_ending->getCodename())) {
102         m_ending->setState(LevelNode::STATE_SOLVED);
103     }
104     else {
105         m_ending->setState(LevelNode::STATE_OPEN);
106     }
107     m_ending->setDepth(-1);
108 }
109 //-----------------------------------------------------------------
110 /**
111  * Store best solution author.
112  * @param codename level codename
113  * @param moves number of moves in solution
114  * @param author solution author
115  */
116 void
bestSolution(const std::string & codename,int moves,const std::string & author)117 WorldBranch::bestSolution(const std::string &codename, int moves,
118                 const std::string &author)
119 {
120     LevelNode *node = m_root->findNamed(codename);
121     if (node) {
122         node->bestSolution(moves, author);
123     }
124     else {
125         LOG_WARNING(ExInfo("there is no such node")
126                 .addInfo("codename", codename)
127                 .addInfo("moves", moves)
128                 .addInfo("author", author));
129     }
130 }
131 //-----------------------------------------------------------------
132 /**
133  * Returns true when level will solved in the past.
134  */
135 bool
wasSolved(const std::string & codename)136 WorldBranch::wasSolved(const std::string &codename)
137 {
138     Path solved =
139         Path::dataReadPath(LevelStatus::getSolutionFilename(codename));
140     return solved.exists();
141 }
142 //-----------------------------------------------------------------
143 /**
144  * Set node state.
145  * @param hidden whether node is start node of hidden branch
146  */
147     void
prepareNode(LevelNode * node,bool hidden)148 WorldBranch::prepareNode(LevelNode *node, bool hidden)
149 {
150     if (wasSolved(node->getCodename())) {
151         node->setState(LevelNode::STATE_SOLVED);
152     }
153     else if (hidden) {
154         node->setState(LevelNode::STATE_HIDDEN);
155     }
156     else {
157         node->setState(LevelNode::STATE_FAR);
158     }
159 }
160 //-----------------------------------------------------------------
161 /**
162  * Insert node as parent child.
163  * @throws LogicException when error occurs
164  */
165 void
insertNode(const std::string & parent,LevelNode * new_node)166 WorldBranch::insertNode(const std::string &parent, LevelNode *new_node)
167 {
168     try {
169         if (parent == "" && m_root) {
170             throw LogicException(ExInfo("there is a one root node already")
171                     .addInfo("root", m_root->getCodename())
172                     .addInfo("new_node", new_node->getCodename()));
173         }
174 
175         if (m_root) {
176             LevelNode *parentNode = m_root->findNamed(parent);
177             if (parentNode) {
178                 parentNode->addChild(new_node);
179             }
180             else {
181                 throw LogicException(ExInfo("there is no such parent node")
182                         .addInfo("parent", parent)
183                         .addInfo("new_node", new_node->getCodename()));
184             }
185         }
186         else {
187             if (parent != "") {
188                 LOG_WARNING(ExInfo("root node should have empty parent")
189                         .addInfo("parent", parent)
190                         .addInfo("new_node", new_node->getCodename()));
191             }
192             m_root = new_node;
193         }
194     }
195     catch (...) {
196         delete new_node;
197         throw;
198     }
199 }
200 
201