1 /*
2 Copyright (C) 2007, 2010 - Bit-Blot
3 
4 This file is part of Aquaria.
5 
6 Aquaria is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 #include "DSQ.h"
22 #include "Game.h"
23 
24 static std::string baseModPath = "./_mods/";
25 
refreshBaseModPath()26 void refreshBaseModPath()
27 {
28 #if defined(BBGE_BUILD_UNIX)
29 	baseModPath = dsq->getUserDataFolder() + "/_mods/";
30 #endif
31 }
32 
Mod()33 Mod::Mod()
34 {
35 	clear();
36 
37 	enqueueModStart = 0;
38 
39 	shuttingDown = false;
40 }
41 
~Mod()42 Mod::~Mod()
43 {
44 	modcache.clean();
45 }
46 
47 /*
48 queue for actual stop and recache
49 which happens in game::applystate
50 */
shutdown()51 void Mod::shutdown()
52 {
53 	shuttingDown = true;
54 }
55 
isShuttingDown()56 bool Mod::isShuttingDown()
57 {
58 	return shuttingDown;
59 }
60 
clear()61 void Mod::clear()
62 {
63 	active = false;
64 	doRecache = 0;
65 	debugMenu = false;
66 	hasMap = false;
67 	blockEditor = false;
68 	mapRevealMethod = REVEAL_UNSPECIFIED;
69 }
70 
isDebugMenu()71 bool Mod::isDebugMenu()
72 {
73 	return debugMenu;
74 }
75 
hasWorldMap()76 bool Mod::hasWorldMap()
77 {
78 	return hasMap;
79 }
80 
isEditorBlocked()81 bool Mod::isEditorBlocked()
82 {
83 	return blockEditor;
84 }
85 
loadModXML(XMLDocument * d,std::string modName)86 bool Mod::loadModXML(XMLDocument *d, std::string modName)
87 {
88 	return readXML((baseModPath + modName + ".xml").c_str(), *d) == XML_SUCCESS;
89 
90 }
91 
getBaseModPath() const92 const std::string& Mod::getBaseModPath() const
93 {
94 	refreshBaseModPath();
95 
96 	return baseModPath;
97 }
98 
load(const std::string & p)99 void Mod::load(const std::string &p)
100 {
101 	clear();
102 
103 	refreshBaseModPath();
104 
105 	name = p;
106 	path = baseModPath + p + "/";
107 
108 	setLocalisationModPath(path);
109 
110 	setActive(true);
111 
112 	XMLDocument d;
113 	loadModXML(&d, p);
114 
115 	XMLElement *mod = d.FirstChildElement("AquariaMod");
116 	if (mod)
117 	{
118 		XMLElement *props = mod->FirstChildElement("Properties");
119 		if (props)
120 		{
121 			props->QueryIntAttribute("recache", &doRecache);
122 			props->QueryIntAttribute("debugMenu", &debugMenu);
123 			props->QueryBoolAttribute("hasWorldMap", &hasMap);
124 			props->QueryBoolAttribute("blockEditor", &blockEditor);
125 
126 			if (props->BoolAttribute("runBG"))
127 				core->settings.runInBackground = true;
128 
129 			if (props->Attribute("worldMapRevealMethod"))
130 				mapRevealMethod = (WorldMapRevealMethod) props->IntAttribute("worldMapRevealMethod");
131 		}
132 	}
133 
134 	dsq->secondaryTexturePath = path + "graphics/";
135 
136 	dsq->sound->audioPath2 = path + "audio/";
137 	dsq->sound->setVoicePath2(path + "audio/");
138 
139 	SkeletalSprite::secondaryAnimationPath = path + "animations/";
140 
141 	dsq->particleBank2 = path + "particles/";
142 	dsq->shotBank2 = path + "shots/";
143 
144 	Shot::loadShotBank(dsq->shotBank1, dsq->shotBank2);
145 	particleManager->loadParticleBank(dsq->particleBank1, dsq->particleBank2);
146 }
147 
getPath() const148 const std::string& Mod::getPath() const
149 {
150 	return path;
151 }
152 
getName() const153 const std::string& Mod::getName() const
154 {
155 	return name;
156 }
157 
recache()158 void Mod::recache()
159 {
160 	if(doRecache)
161 	{
162 		dsq->precacher.clean();
163 		dsq->unloadResources();
164 	}
165 
166 	if(active)
167 	{
168 		modcache.setBaseDir(dsq->secondaryTexturePath);
169 		std::string fname = path;
170 		if(fname[fname.length() - 1] != '/')
171 			fname += '/';
172 		fname += "precache.txt";
173 		fname = core->adjustFilenameCase(fname);
174 		if (exists(fname))
175 		{
176 			modcache.precacheList(fname);
177 			core->resetTimer();
178 		}
179 	}
180 	else
181 	{
182 		modcache.clean();
183 	}
184 
185 	if(doRecache)
186 	{
187 		dsq->precacher.precacheList("data/precache.txt");
188 		dsq->reloadResources();
189 		core->resetTimer();
190 	}
191 }
192 
start()193 void Mod::start()
194 {
195 	dsq->overlay->color = 0;
196 
197 	dsq->toggleVersionLabel(0);
198 	dsq->toggleCursor(0, 1);
199 
200 	float t = 1;
201 	dsq->overlay->alpha.interpolateTo(1, t);
202 	core->sound->fadeMusic(SFT_OUT, t*0.9f);
203 
204 	core->main(t);
205 
206 	core->sound->stopMusic();
207 
208 	enqueueModStart = 1;
209 	dsq->recentSaveSlot = -1;
210 }
211 
applyStart()212 void Mod::applyStart()
213 {
214 	enqueueModStart = 0;
215 
216 	core->popAllStates();
217 	core->clearGarbage();
218 	recache();
219 	dsq->continuity.reset();
220 	dsq->scriptInterface.reset();
221 
222 	// load the mod-init.lua file
223 	// which is in the root of the mod's folder
224 	// e.g. _mods/recachetest/
225 	std::string scriptPath = path + "mod-init.lua";
226 	debugLog("scriptPath: " + scriptPath);
227 	if (!dsq->runScript(scriptPath, "init"))
228 	{
229 		debugLog("MOD: runscript failed");
230 		setActive(false);
231 		dsq->title();
232 	}
233 	if (isActive() && dsq->game->sceneToLoad.empty())
234 	{
235 		debugLog("MOD: no scene loaded in mod-init");
236 		setActive(false);
237 		dsq->title();
238 	}
239 	else if (isActive())
240 	{
241 	}
242 }
243 
isActive()244 bool Mod::isActive()
245 {
246 	return active;
247 }
248 
setActive(bool a)249 void Mod::setActive(bool a)
250 {
251 	bool wasActive = active;
252 
253 	active = a;
254 
255 	if (wasActive != active)
256 	{
257 		if (!active)
258 		{
259 			dsq->unloadMods();
260 
261 			mapRevealMethod = REVEAL_UNSPECIFIED;
262 			setLocalisationModPath("");
263 			name = path = "";
264 			dsq->secondaryTexturePath = "";
265 			dsq->sound->audioPath2 = "";
266 			dsq->sound->setVoicePath2("");
267 			SkeletalSprite::secondaryAnimationPath = "";
268 
269 			dsq->particleBank2 = "";
270 			dsq->shotBank2 = "";
271 
272 			Shot::loadShotBank(dsq->shotBank1, dsq->shotBank2);
273 			particleManager->loadParticleBank(dsq->particleBank1, dsq->particleBank2);
274 
275 			dsq->setFilter(dsq->dsq_filter);
276 
277 			recache();
278 		}
279 		dsq->game->loadEntityTypeList();
280 	}
281 }
282 
stop()283 void Mod::stop()
284 {
285 	setActive(false);
286 
287 	core->settings.runInBackground = false;
288 	debugMenu = false;
289 	shuttingDown = false;
290 	dsq->scriptInterface.reset();
291 	dsq->game->setWorldPaused(false);
292 }
293 
update(float dt)294 void Mod::update(float dt)
295 {
296 	if (enqueueModStart)
297 	{
298 		enqueueModStart = 0;
299 
300 		applyStart();
301 	}
302 }
303 
getTypeFromXML(XMLElement * xml)304 ModType Mod::getTypeFromXML(XMLElement *xml) // should be <AquariaMod>...</AquariaMod> - element
305 {
306 	if(xml)
307 	{
308 		XMLElement *prop = xml->FirstChildElement("Properties");
309 		if(prop)
310 		{
311 			const char *type = prop->Attribute("type");
312 			if(type)
313 			{
314 				if(!strcmp(type, "mod"))
315 					return MODTYPE_MOD;
316 				else if(!strcmp(type, "patch"))
317 					return MODTYPE_PATCH;
318 				else
319 				{
320 					std::ostringstream os;
321 					os << "Unknown mod type '" << type << "' in XML, default to MODTYPE_MOD";
322 					debugLog(os.str());
323 				}
324 			}
325 		}
326 	}
327 	return MODTYPE_MOD; // the default
328 }
329