mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-01-24 17:26:41 +00:00
97cea29235
Note that this makes the world map highly inefficient for now because the number of draw calls goes up crazy high. Will investigate.
359 lines
6.6 KiB
C++
359 lines
6.6 KiB
C++
/*
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include "DSQ.h"
|
|
#include "Game.h"
|
|
#include "ReadXML.h"
|
|
#include "Shot.h"
|
|
|
|
static std::string baseModPath = "./_mods/";
|
|
|
|
void refreshBaseModPath()
|
|
{
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
baseModPath = dsq->getUserDataFolder() + "/_mods/";
|
|
#endif
|
|
}
|
|
|
|
Mod::Mod()
|
|
{
|
|
clear();
|
|
|
|
enqueueModStart = 0;
|
|
|
|
shuttingDown = false;
|
|
}
|
|
|
|
Mod::~Mod()
|
|
{
|
|
modcache.clear();
|
|
}
|
|
|
|
/*
|
|
queue for actual stop and recache
|
|
which happens in game::applystate
|
|
*/
|
|
void Mod::shutdown()
|
|
{
|
|
shuttingDown = true;
|
|
}
|
|
|
|
bool Mod::isShuttingDown()
|
|
{
|
|
return shuttingDown;
|
|
}
|
|
|
|
void Mod::clear()
|
|
{
|
|
active = false;
|
|
debugMenu = false;
|
|
hasMap = false;
|
|
blockEditor = false;
|
|
compatScript = "";
|
|
}
|
|
|
|
bool Mod::isDebugMenu() const
|
|
{
|
|
return debugMenu;
|
|
}
|
|
|
|
bool Mod::hasWorldMap() const
|
|
{
|
|
return hasMap;
|
|
}
|
|
|
|
bool Mod::isEditorBlocked() const
|
|
{
|
|
return blockEditor;
|
|
}
|
|
|
|
bool Mod::loadModXML(XMLDocument *d, std::string modName)
|
|
{
|
|
bool ok = readXML((baseModPath + modName + "/mod-info.xml").c_str(), *d) == XML_SUCCESS;
|
|
if(!ok)
|
|
ok = readXML((baseModPath + modName + ".xml").c_str(), *d) == XML_SUCCESS;
|
|
return ok;
|
|
|
|
}
|
|
|
|
const std::string& Mod::getBaseModPath() const
|
|
{
|
|
refreshBaseModPath();
|
|
|
|
return baseModPath;
|
|
}
|
|
|
|
bool Mod::loadSavedGame(const std::string& path)
|
|
{
|
|
load(path);
|
|
if(loadCompatScript())
|
|
return true;
|
|
|
|
debugLog("MOD: loadSavedGame/compatScript failed");
|
|
setActive(false);
|
|
dsq->title(false);
|
|
return false;
|
|
}
|
|
|
|
void Mod::load(const std::string &p)
|
|
{
|
|
clear();
|
|
|
|
refreshBaseModPath();
|
|
|
|
name = p;
|
|
path = baseModPath + p + "/";
|
|
|
|
setLocalisationModPath(path);
|
|
|
|
setActive(true);
|
|
|
|
XMLDocument d;
|
|
loadModXML(&d, p);
|
|
|
|
XMLElement *mod = d.FirstChildElement("AquariaMod");
|
|
if (mod)
|
|
{
|
|
XMLElement *props = mod->FirstChildElement("Properties");
|
|
if (props)
|
|
{
|
|
props->QueryIntAttribute("debugMenu", &debugMenu);
|
|
props->QueryBoolAttribute("hasWorldMap", &hasMap);
|
|
props->QueryBoolAttribute("blockEditor", &blockEditor);
|
|
|
|
if (props->BoolAttribute("runBG"))
|
|
core->settings.runInBackground = true;
|
|
}
|
|
XMLElement *compat = mod->FirstChildElement("Compatibility");
|
|
if(compat)
|
|
{
|
|
if(const char *script = compat->Attribute("script"))
|
|
compatScript = script;
|
|
}
|
|
}
|
|
|
|
bool hotReload = dsq->user.system.hotreload;
|
|
dsq->setExtraTexturePath((path + "graphics/").c_str(), hotReload);
|
|
|
|
dsq->sound->audioPath2 = path + "audio/";
|
|
dsq->sound->setVoicePath2(path + "audio/");
|
|
|
|
SkeletalSprite::secondaryAnimationPath = path + "animations/";
|
|
|
|
dsq->particleBank2 = path + "particles/";
|
|
dsq->shotBank2 = path + "shots/";
|
|
|
|
Shot::loadShotBank(dsq->shotBank1, dsq->shotBank2);
|
|
particleManager->loadParticleBank(dsq->particleBank1, dsq->particleBank2);
|
|
}
|
|
|
|
const std::string& Mod::getPath() const
|
|
{
|
|
return path;
|
|
}
|
|
|
|
const std::string& Mod::getName() const
|
|
{
|
|
return name;
|
|
}
|
|
|
|
void Mod::recache()
|
|
{
|
|
core->texmgr.reloadAll(TextureMgr::KEEP_IF_SAME);
|
|
|
|
if(active)
|
|
{
|
|
modcache.setBaseDir(dsq->getExtraTexturePath());
|
|
std::string fname = path;
|
|
if(fname[fname.length() - 1] != '/')
|
|
fname += '/';
|
|
fname += "precache.txt";
|
|
fname = adjustFilenameCase(fname);
|
|
if (exists(fname))
|
|
modcache.precacheList(fname);
|
|
}
|
|
else
|
|
{
|
|
modcache.clear();
|
|
}
|
|
|
|
core->resetTimer();
|
|
}
|
|
|
|
void Mod::start()
|
|
{
|
|
dsq->overlay->color = 0;
|
|
|
|
dsq->toggleVersionLabel(0);
|
|
dsq->toggleCursor(0, 1);
|
|
|
|
float t = 1;
|
|
dsq->overlay->alpha.interpolateTo(1, t);
|
|
core->sound->fadeMusic(SFT_OUT, t*0.9f);
|
|
|
|
core->run(t);
|
|
|
|
core->sound->stopMusic();
|
|
|
|
enqueueModStart = 1;
|
|
dsq->recentSaveSlot = -1;
|
|
}
|
|
|
|
void Mod::applyStart()
|
|
{
|
|
enqueueModStart = 0;
|
|
|
|
core->popAllStates();
|
|
core->clearGarbage();
|
|
recache();
|
|
dsq->continuity.reset();
|
|
dsq->scriptInterface.reset();
|
|
|
|
if(!tryStart())
|
|
{
|
|
setActive(false);
|
|
dsq->title(false);
|
|
}
|
|
}
|
|
|
|
bool Mod::tryStart()
|
|
{
|
|
|
|
// Before loading init.lua, load a compatibility layer, if necessary
|
|
if(!loadCompatScript())
|
|
{
|
|
debugLog("MOD: compatScript failed");
|
|
return false;
|
|
}
|
|
|
|
// load the mod-init.lua file
|
|
// which is in the root of the mod's folder
|
|
// e.g. _mods/recachetest/
|
|
std::string scriptPath = path + "mod-init.lua";
|
|
debugLog("scriptPath: " + scriptPath);
|
|
if (!dsq->runScript(scriptPath, "init"))
|
|
{
|
|
debugLog("MOD: runscript failed");
|
|
return false;
|
|
}
|
|
if (isActive() && game->sceneToLoad.empty())
|
|
{
|
|
debugLog("MOD: no scene loaded in mod-init");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Mod::isActive() const
|
|
{
|
|
return active;
|
|
}
|
|
|
|
void Mod::setActive(bool a)
|
|
{
|
|
bool wasActive = active;
|
|
|
|
active = a;
|
|
|
|
if (wasActive != active)
|
|
{
|
|
if (!active)
|
|
{
|
|
dsq->unloadMods();
|
|
compatScript = "";
|
|
|
|
setLocalisationModPath("");
|
|
name = path = "";
|
|
dsq->setExtraTexturePath(NULL, false);
|
|
dsq->sound->audioPath2 = "";
|
|
dsq->sound->setVoicePath2("");
|
|
SkeletalSprite::secondaryAnimationPath = "";
|
|
|
|
dsq->particleBank2 = "";
|
|
dsq->shotBank2 = "";
|
|
|
|
Shot::loadShotBank(dsq->shotBank1, dsq->shotBank2);
|
|
particleManager->loadParticleBank(dsq->particleBank1, dsq->particleBank2);
|
|
|
|
recache();
|
|
}
|
|
game->loadEntityTypeList();
|
|
}
|
|
}
|
|
|
|
void Mod::stop()
|
|
{
|
|
setActive(false);
|
|
|
|
core->settings.runInBackground = false;
|
|
debugMenu = false;
|
|
shuttingDown = false;
|
|
dsq->scriptInterface.reset();
|
|
game->setWorldPaused(false);
|
|
}
|
|
|
|
void Mod::update(float dt)
|
|
{
|
|
if (enqueueModStart)
|
|
{
|
|
enqueueModStart = 0;
|
|
|
|
applyStart();
|
|
}
|
|
}
|
|
|
|
ModType Mod::getTypeFromXML(XMLElement *xml) // should be <AquariaMod>...</AquariaMod> - element
|
|
{
|
|
if(xml)
|
|
{
|
|
XMLElement *prop = xml->FirstChildElement("Properties");
|
|
if(prop)
|
|
{
|
|
const char *type = prop->Attribute("type");
|
|
if(type)
|
|
{
|
|
if(!strcmp(type, "mod"))
|
|
return MODTYPE_MOD;
|
|
else if(!strcmp(type, "patch"))
|
|
return MODTYPE_PATCH;
|
|
else
|
|
{
|
|
std::ostringstream os;
|
|
os << "Unknown mod type '" << type << "' in XML, default to MODTYPE_MOD";
|
|
debugLog(os.str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return MODTYPE_MOD; // the default
|
|
}
|
|
|
|
bool Mod::loadCompatScript()
|
|
{
|
|
std::string cs = compatScript.c_str();
|
|
if(cs.empty())
|
|
cs = "default";
|
|
if(dsq->runScript("scripts/compat/" + cs + ".lua"))
|
|
return true;
|
|
|
|
dsq->scriptInterface.reset();
|
|
return false;
|
|
}
|