1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-12-01 15:35:47 +00:00
Aquaria/Aquaria/Continuity.cpp
fgenesis 8472718fb7 Major include refactor; changes to pretty much everything
This untangles some of the gigantic kitchen sink headers
in an attempt to split things into smaller files.
Also don't include gl.h, glext.h, windows.h,
and other such nonsense *everywhere*.

Lots of cleanups on the way too. More dead/unused code removal.

Remove incrFlag(), decrFlag() Lua functions.
2016-07-09 04:18:40 +02:00

3551 lines
75 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 "Avatar.h"
#include "ScriptedEntity.h"
#include "GridRender.h"
#include "DeflateCompressor.h"
#include "ttvfs_stdio.h"
#include "ReadXML.h"
#include "Web.h"
#include "tinyxml2.h"
using namespace tinyxml2;
#define MAX_EATS 8
const float webBitTime = 2;
const float defenseTime = 15;
const float speedTime = 30;
const float biteTime = 30;
const float fishPoisonTime = 30;
const float energyTime = 45;
const float webTime = 8;
const float petPowerTime = 30;
const float lightTime = 60;
Profile::Profile()
{
name = "save";
}
Continuity::Continuity()
{
toggleMoveMode = false;
poisonBitTime = 1;
poisonBitTimeAvatar = 2;
statsAndAchievements = 0;
}
bool Continuity::isIngredientFull(IngredientData *data)
{
for (int i = 0; i < ingredients.size(); i++)
{
if (nocasecmp(ingredients[i]->name, data->name)==0)
{
if (ingredients[i]->amount >= ingredients[i]->maxAmount)
return true;
else
return false;
}
}
return false;
}
void Continuity::pickupIngredient(IngredientData *d, int amount, bool effects, bool learn)
{
if(learn)
learnRecipe(d->name, effects);
if (!getIngredientHeldByName(d->name))
{
ingredients.push_back(d);
}
if (d->amount < d->maxAmount - amount)
{
d->amount += amount;
}
else
{
d->amount = d->maxAmount;
}
}
int Continuity::indexOfIngredientData(const IngredientData* data) const
{
for (int i = 0; i < ingredientData.size(); i++)
{
if (ingredientData[i]->name == data->name)
{
return i;
}
}
return -1;
}
#define FOR_INGREDIENTDATA(x) for (int x = 0; x < ingredientData.size(); x++)
IngredientData *Continuity::getIngredientDataByName(const std::string &name)
{
FOR_INGREDIENTDATA(i)
{
if (nocasecmp(ingredientData[i]->name, name)==0)
return ingredientData[i];
}
return 0;
}
IngredientData *Continuity::getIngredientHeldByName(const std::string &name) const
{
for (int i = 0; i < ingredients.size(); i++) {
if (nocasecmp(ingredients[i]->name, name)==0)
return ingredients[i];
}
return 0;
}
IngredientType Continuity::getIngredientTypeFromName(const std::string &name) const
{
if (name == "Meat")
return IT_MEAT;
else if (name == "Oil")
return IT_OIL;
else if (name == "Egg")
return IT_EGG;
else if (name == "Part")
return IT_PART;
else if (name == "Bone")
return IT_BONE;
else if (name == "Shell")
return IT_SHELL;
else if (name == "Tentacle")
return IT_TENTACLE;
else if (name == "Berry")
return IT_BERRY;
else if (name == "Leaf")
return IT_LEAF;
else if (name == "Poultice")
return IT_POULTICE;
else if (name == "IceChunk")
return IT_ICECHUNK;
else if (name == "Bulb")
return IT_BULB;
else if (name == "Roll")
return IT_ROLL;
else if (name == "Soup")
return IT_SOUP;
else if (name == "Cake")
return IT_CAKE;
else if (name == "IceCream")
return IT_ICECREAM;
else if (name == "Loaf")
return IT_LOAF;
else if (name == "PerogiType")
return IT_PEROGI;
else if (name == "Mushroom")
return IT_MUSHROOM;
else if (name == "Anything")
return IT_ANYTHING;
else if (name.length() && isdigit(name[0]))
return (IngredientType)atoi(name.c_str());
return IT_NONE;
}
std::string Continuity::getIngredientDisplayName(const std::string& name) const
{
IngredientNameMap::const_iterator it = ingredientDisplayNames.find(name);
if (it != ingredientDisplayNames.end())
return it->second;
return splitCamelCase(name);
}
IngredientData *Continuity::getIngredientHeldByIndex(int idx) const
{
if (idx < 0 || idx >= ingredients.size()) return 0;
return ingredients[idx];
}
IngredientData *Continuity::getIngredientDataByIndex(int idx)
{
if (idx < 0 || idx >= ingredientData.size()) return 0;
return ingredientData[idx];
}
int Continuity::getIngredientDataSize() const
{
return (int)ingredientData.size();
}
int Continuity::getIngredientHeldSize() const
{
return (int)ingredients.size();
}
void Continuity::initFoodSort()
{
// move to init
sortByType.clear();
sortByType.push_back(FoodSortOrder(IT_POULTICE));
sortByType.push_back(FoodSortOrder(IT_ROLL));
sortByType.push_back(FoodSortOrder(IT_CAKE));
sortByType.push_back(FoodSortOrder(IT_SOUP));
sortByType.push_back(FoodSortOrder(IT_LOAF));
sortByType.push_back(FoodSortOrder(IT_PEROGI));
sortByType.push_back(FoodSortOrder(IT_LEAF));
sortByType.push_back(FoodSortOrder(IT_MEAT));
sortByType.push_back(FoodSortOrder(IT_OIL));
sortByType.push_back(FoodSortOrder(IT_ICECREAM));
sortByType.push_back(FoodSortOrder(IT_BERRY));
sortByType.push_back(FoodSortOrder(IT_MUSHROOM));
sortByType.push_back(FoodSortOrder(IT_BULB));
sortByType.push_back(FoodSortOrder(IT_EGG));
sortByType.push_back(FoodSortOrder(IT_SHELL));
sortByType.push_back(FoodSortOrder(IT_PART));
sortByType.push_back(FoodSortOrder(IT_TENTACLE));
sortByType.push_back(FoodSortOrder(IT_ICECHUNK));
sortByType.push_back(FoodSortOrder(IT_BONE));
sortByType.push_back(FoodSortOrder(IT_FOOD));
sortByHeal.clear();
sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_MAXHP));
for (int i = 10; i >= -10; i--)
{
if (i != 0)
sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_HP, "", i));
}
sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_DEFENSE));
sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_SPEED));
sortByIngredients.clear();
for (int i = 0; i < IT_INGREDIENTSEND; i++)
{
sortByIngredients.push_back(FoodSortOrder((IngredientType)i));
}
}
void Continuity::sortFood()
{
std::vector<FoodSortOrder> sortOrder;
bool doSort = true;
switch (dsq->continuity.foodSortType)
{
case FOODSORT_BYTYPE:
sortOrder = sortByType;
break;
case FOODSORT_BYHEAL:
sortOrder = sortByHeal;
break;
case FOODSORT_BYINGREDIENT:
sortOrder = sortByIngredients;
break;
}
if (doSort)
{
std::vector<IngredientData*> sort;
for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
{
dsq->continuity.ingredients[i]->sorted = false;
}
for (int j = 0; j < sortOrder.size(); j++)
{
for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
{
IngredientData *data = dsq->continuity.ingredients[i];
if (!data->sorted)
{
if (sortOrder[j].type == IT_NONE || sortOrder[j].type == data->type)
{
if (!sortOrder[j].name.empty())
{
if (sortOrder[j].name == data->name)
{
data->sorted = true;
sort.push_back(data);
}
}
else if (sortOrder[j].effectType != IET_NONE)
{
for (int c = 0; c < data->effects.size(); c++)
{
if (data->effects[c].type == sortOrder[j].effectType)
{
if (sortOrder[j].effectAmount == 0 || data->effects[c].magnitude == sortOrder[j].effectAmount)
{
data->sorted = true;
sort.push_back(data);
}
}
}
}
else
{
data->sorted = true;
sort.push_back(data);
}
}
}
}
}
for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
{
IngredientData *data = dsq->continuity.ingredients[i];
if (!data->sorted)
{
data->sorted = true;
sort.push_back(data);
}
}
ingredients.clear();
for (int i = 0; i < sort.size(); i++) {
ingredients.push_back(sort[i]);
}
sort.clear();
}
}
void Continuity::setRegen(float t)
{
regenTimer.start(t);
}
void Continuity::setTrip(float t)
{
tripTimer.start(t);
dsq->game->avatar->applyTripEffects();
}
void Continuity::setInvincible(float t)
{
invincibleTimer.start(t);
}
void Continuity::setSpeedMultiplier(float s, float t)
{
speedMultTimer.start(t);
speedMult = s;
}
void Continuity::setEnergy(float m, float t)
{
energyTimer.start(t);
energyMult = m;
}
void Continuity::setLiPower(float m, float t)
{
liPowerTimer.start(t);
liPower = m;
}
void Continuity::setPetPower(float m, float t)
{
petPower = m;
petPowerTimer.start(t);
}
void Continuity::setLight(float m, float t)
{
light = m;
lightTimer.start(t);
}
void Continuity::setWeb(float t)
{
webTimer.start(t);
webBitTimer.start(webBitTime);
if (dsq->game->avatar)
{
if (!dsq->game->avatar->web)
{
dsq->game->avatar->createWeb();
}
}
}
void Continuity::setPoison(float m, float t)
{
poisonTimer.start(t);
poison = m;
if (poison)
{
poisonBitTimer.start(poisonBitTime);
}
}
void Continuity::cureAllStatus()
{
setPoison(0,0);
if (dsq->game->avatar)
{
dsq->game->avatar->setBlind(0);
}
}
void Continuity::setDefenseMultiplier(float s, float t)
{
defenseMultTimer.start(t);
defenseMult = s;
}
void Continuity::setBiteMultiplier(float m, float t)
{
biteMultTimer.start(t);
biteMult = m;
}
void Continuity::setFishPoison(float m, float t)
{
fishPoisonTimer.start(t);
fishPoison = m;
}
std::string Continuity::getIEString(IngredientData *data, int i)
{
if (i < 0 || i >= data->effects.size()) return "";
IngredientEffect fx = data->effects[i];
IngredientEffectType useType = fx.type;
std::ostringstream os;
switch(useType)
{
case IET_HP:
if (fx.magnitude > 0)
{
std::ostringstream os;
os << dsq->continuity.stringBank.get(200) << " ";
os << dsq->continuity.stringBank.get(100) << " ";
os << fx.magnitude;
return os.str();
}
else
{
std::ostringstream os;
os << dsq->continuity.stringBank.get(200) << " ";
os << dsq->continuity.stringBank.get(101) << " ";
os << fabsf(fx.magnitude);
return os.str();
}
break;
case IET_MAXHP:
return dsq->continuity.stringBank.get(201);
break;
case IET_DEFENSE:
os << dsq->continuity.stringBank.get(202);
os << " " << fx.magnitude << " " << dsq->continuity.stringBank.get(205) << " " << defenseTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_SPEED:
os << dsq->continuity.stringBank.get(204) << " " << fx.magnitude;
os << " " << dsq->continuity.stringBank.get(205) << " " << speedTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_REGEN:
os << dsq->continuity.stringBank.get(206) << " " << fx.magnitude;
return os.str();
break;
case IET_TRIP:
return dsq->continuity.stringBank.get(207);
break;
case IET_EAT:
return dsq->continuity.stringBank.get(208);
break;
case IET_BITE:
os << dsq->continuity.stringBank.get(209);
os << " " << dsq->continuity.stringBank.get(205) << " " << biteTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_FISHPOISON:
os << dsq->continuity.stringBank.get(217);
os << " " << dsq->continuity.stringBank.get(205) << " " << fishPoisonTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_INVINCIBLE:
os << dsq->continuity.stringBank.get(210);
os << " " << dsq->continuity.stringBank.get(205) << " " << (fx.magnitude*5) << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_ENERGY:
os << dsq->continuity.stringBank.get(211) << " " << fx.magnitude;
os << " " << dsq->continuity.stringBank.get(205) << " " << energyTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_BLIND:
return dsq->continuity.stringBank.get(212);
break;
case IET_POISON:
if (fx.magnitude < 0)
return dsq->continuity.stringBank.get(213);
else
return dsq->continuity.stringBank.get(214);
break;
case IET_YUM:
return dsq->continuity.stringBank.get(215);
break;
case IET_WEB:
os << dsq->continuity.stringBank.get(219);
os << " " << dsq->continuity.stringBank.get(205) << " " << webTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_ALLSTATUS:
return dsq->continuity.stringBank.get(218);
break;
case IET_PETPOWER:
os << dsq->continuity.stringBank.get(216);
os << " " << dsq->continuity.stringBank.get(205) << " " << petPowerTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_LIGHT:
os << dsq->continuity.stringBank.get(220);
os << " " << dsq->continuity.stringBank.get(205) << " " << lightTime << " " << dsq->continuity.stringBank.get(203);
return os.str();
break;
case IET_LI:
return dsq->continuity.stringBank.get(227);
break;
case IET_SCRIPT:
if(dsq->game->cookingScript)
{
std::string ret = "";
dsq->game->cookingScript->call("getIngredientEffectString", data->name.c_str(), &ret);
return ret;
}
break;
}
return "";
}
std::string Continuity::getAllIEString(IngredientData *data)
{
std::ostringstream os;
for (int i = 0; i < data->effects.size(); i++)
{
os << getIEString(data, i) << "\n";
}
return os.str();
}
// returns true if eaten
bool Continuity::applyIngredientEffects(IngredientData *data)
{
bool eaten = true;
float y =0;
for (int i = 0; i < data->effects.size(); i++)
{
y = 300 + i * 40;
IngredientEffect fx = data->effects[i];
IngredientEffectType useType = fx.type;
if (fx.type == IET_RANDOM)
{
}
switch(useType)
{
case IET_HP:
{
dsq->game->avatar->heal(fx.magnitude);
debugLog("ingredient effect: hp");
if (fx.magnitude > 0)
{
dsq->centerMessage(getIEString(data, i), y);
core->sound->playSfx("CollectMana");
dsq->overlay2->color = Vector(0.5, 0.5, 1);
dsq->overlay2->alpha.ensureData();
dsq->overlay2->alpha.data->path.clear();
dsq->overlay2->alpha.data->path.addPathNode(0, 0);
dsq->overlay2->alpha.data->path.addPathNode(0.5, 0.5);
dsq->overlay2->alpha.data->path.addPathNode(0, 1);
dsq->overlay2->alpha.startPath(1);
}
else
{
dsq->centerMessage(getIEString(data, i), y, 1);
dsq->game->avatar->playHitSound();
}
}
break;
case IET_MAXHP:
{
dsq->game->avatar->heal(dsq->game->avatar->maxHealth);
debugLog("ingredient effect: maxhp");
core->sound->playSfx("CollectMana");
dsq->overlay2->color = Vector(0.5, 0.5, 1);
dsq->overlay2->alpha.ensureData();
dsq->overlay2->alpha.data->path.clear();
dsq->overlay2->alpha.data->path.addPathNode(0, 0);
dsq->overlay2->alpha.data->path.addPathNode(0.5, 0.5);
dsq->overlay2->alpha.data->path.addPathNode(0, 1);
dsq->overlay2->alpha.startPath(2);
dsq->centerMessage(getIEString(data, i), y);
}
break;
case IET_DEFENSE:
{
debugLog("ingredient effect: defense");
if (fx.magnitude <= 1)
dsq->continuity.setDefenseMultiplier(0.75, defenseTime);
else if (fx.magnitude == 2)
dsq->continuity.setDefenseMultiplier(0.5, defenseTime);
else if (fx.magnitude == 3)
dsq->continuity.setDefenseMultiplier(0.3, defenseTime);
else
debugLog("unsupported magnitude for defense");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("defense");
}
break;
case IET_SPEED:
{
dsq->continuity.setSpeedMultiplier(1.0f + fx.magnitude*0.5f, speedTime);
debugLog("ingredient effect: speed");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("speedup");
}
break;
case IET_REGEN:
{
dsq->continuity.setRegen(fx.magnitude*5);
debugLog("ingredient effect: regen");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("regen");
}
break;
case IET_TRIP:
{
dsq->continuity.setTrip(fx.magnitude*30);
debugLog("ingredient effect: trip");
dsq->centerMessage(getIEString(data, i), y);
}
break;
case IET_EAT:
{
EatData *getter = dsq->continuity.getEatData(fx.string);
if (getter)
{
EatData setter = *getter;
dsq->continuity.eatBeast(setter);
debugLog("ate: " + setter.name);
}
debugLog("ingredient effect: eat");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("gulp");
}
break;
case IET_BITE:
{
dsq->continuity.setBiteMultiplier(1.0f + fx.magnitude, biteTime);
debugLog("ingredient effect: bite");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("bite");
}
break;
case IET_FISHPOISON:
{
dsq->continuity.setFishPoison(1.0f * fx.magnitude, fishPoisonTime);
debugLog("ingredient effect: fishPoison");
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("poison");
}
break;
case IET_INVINCIBLE:
{
dsq->continuity.setInvincible(fx.magnitude*5);
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("invincible");
}
break;
case IET_ENERGY:
{
dsq->continuity.setEnergy(fx.magnitude, energyTime);
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("energy");
}
break;
case IET_BLIND:
{
if (fx.magnitude < 0)
{
dsq->game->avatar->setBlind(0);
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("regen");
}
}
break;
case IET_POISON:
{
if (fx.magnitude < 0)
{
dsq->continuity.setPoison(0,0);
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("regen");
}
else
{
dsq->sound->playSfx("poison");
float t = 30;
dsq->continuity.setPoison(fx.magnitude,t);
dsq->centerMessage(getIEString(data, i), y, 1);
}
}
break;
case IET_YUM:
{
dsq->centerMessage(getIEString(data, i), y);
dsq->sound->playSfx("naijayum");
}
break;
case IET_WEB:
{
dsq->sound->playSfx("spiderweb");
dsq->centerMessage(getIEString(data, i), y);
dsq->continuity.setWeb(webTime);
}
break;
case IET_ALLSTATUS:
{
dsq->sound->playSfx("regen");
dsq->continuity.cureAllStatus();
dsq->centerMessage(getIEString(data, i), y);
}
break;
case IET_PETPOWER:
{
dsq->sound->playSfx("nautilus");
dsq->continuity.setPetPower(fx.magnitude, petPowerTime);
dsq->centerMessage(getIEString(data, i), y);
}
break;
case IET_LIGHT:
{
dsq->sound->playSfx("sunform");
dsq->continuity.setLight(fx.magnitude, lightTime);
dsq->centerMessage(getIEString(data, i), y);
}
break;
case IET_LI:
{
// this should do nothing, its just here to catch the ingredient effect so it doesn't
// give the "default:" error message
// this item should only affect li if naija drops it and li eats it.
}
break;
case IET_SCRIPT:
{
// If this fails, it will still be eaten
if(dsq->game->cookingScript)
dsq->game->cookingScript->call("useIngredient", data->name.c_str(), &eaten);
}
break;
default:
{
char str[256];
sprintf((char*)&str, "ingredient effect not defined, index[%d]", int(useType));
errorLog(str);
eaten = false;
}
break;
}
}
return eaten;
}
std::string Continuity::getIngredientAffectsString(IngredientData *data)
{
return getAllIEString(data);
}
void Continuity::loadTreasureData()
{
treasureData.clear();
std::string line, gfx, file;
int num, use;
float sz;
bool found = false;
if (dsq->mod.isActive())
{
file = dsq->mod.getPath() + "treasures.txt";
if(exists(file))
found = true;
}
if(!found)
file = "data/treasures.txt";
InStream in2(file.c_str());
while (std::getline(in2, line))
{
std::istringstream is(line);
is >> num >> gfx >> sz >> use;
if (sz == 0)
sz = 1;
TreasureDataEntry d;
d.gfx = gfx;
d.sz = sz;
d.use = use;
treasureData[num] = d;
}
in2.close();
}
void Continuity::clearIngredientData()
{
for (IngredientDatas::iterator i = ingredientData.begin(); i != ingredientData.end(); ++ i)
{
delete *i;
}
ingredientData.clear();
}
void Continuity::loadIngredientData()
{
if(ingredients.size())
{
debugLog("Can't reload ingredient data, inventory is not empty");
return; // ... because otherwise there would be dangling pointers and it would crash.
}
clearIngredientData();
ingredientDisplayNames.clear();
recipes.clear();
loadIngredientDisplayNames("data/ingredientnames.txt");
std::string fname = localisePath("data/ingredientnames.txt");
loadIngredientDisplayNames(fname);
if(dsq->mod.isActive())
{
fname = localisePath(dsq->mod.getPath() + "ingredientnames.txt", dsq->mod.getPath());
loadIngredientDisplayNames(fname);
}
if(dsq->mod.isActive())
{
//load mod ingredients
loadIngredientData(dsq->mod.getPath() + "ingredients.txt");
}
//load ingredients for the main game
if(ingredientData.empty() && recipes.empty())
{
loadIngredientData("data/ingredients.txt");
}
}
void Continuity::loadIngredientData(const std::string &file)
{
std::string line, name, gfx, type, effects;
clearIngredientData();
recipes.clear();
InStream in(file.c_str());
bool recipes = false;
bool extradata = false;
while (std::getline(in, line))
{
std::istringstream inLine(line);
inLine >> name;
if (name == "==Recipes==")
{
recipes = true;
break;
}
else if(name == "==Extra==")
{
extradata = true;
break;
}
inLine >> gfx >> type;
std::getline(inLine, effects);
IngredientData *data = new IngredientData(name, gfx, getIngredientTypeFromName(type));
if (!effects.empty())
{
int p1 = effects.find("(");
int p2 = effects.find(")");
if (p1 != std::string::npos && p2 != std::string::npos)
{
effects = effects.substr(p1+1, p2-(p1+1));
std::istringstream fxLine(effects);
std::string bit;
while (fxLine >> bit)
{
IngredientEffect fx;
if (bit.find("eat:") != std::string::npos)
{
int pos = bit.find(':')+1;
fx.string = bit.substr(pos, bit.size()-pos);
fx.type = IET_EAT;
}
else if (bit.find("yum") != std::string::npos)
{
fx.type = IET_YUM;
}
else if (bit.find("petpower") != std::string::npos)
{
fx.type = IET_PETPOWER;
}
else if (bit.find("web") != std::string::npos)
{
fx.type = IET_WEB;
}
else if (bit.find("energy") != std::string::npos)
{
fx.type = IET_ENERGY;
}
else if (bit.find("poison") != std::string::npos)
{
fx.type = IET_POISON;
}
else if (bit.find("blind") != std::string::npos)
{
fx.type = IET_BLIND;
}
else if (bit.find("allstatus") != std::string::npos)
{
fx.type = IET_ALLSTATUS;
}
else if (bit.find("maxhp") != std::string::npos)
{
fx.type = IET_MAXHP;
}
else if (bit.find("invincible") != std::string::npos)
{
fx.type = IET_INVINCIBLE;
}
else if (bit.find("trip") != std::string::npos)
{
fx.type = IET_TRIP;
}
else if (bit.find("defense") != std::string::npos)
{
fx.type = IET_DEFENSE;
}
else if (bit.find("speed") != std::string::npos)
{
fx.type = IET_SPEED;
}
else if (bit.find("random") != std::string::npos)
{
fx.type = IET_RANDOM;
}
else if (bit.find("bite") != std::string::npos)
{
fx.type = IET_BITE;
}
else if (bit.find("fishPoison") != std::string::npos)
{
fx.type = IET_FISHPOISON;
}
else if (bit.find("regen") != std::string::npos)
{
fx.type = IET_REGEN;
}
else if (bit.find("light") != std::string::npos)
{
fx.type = IET_LIGHT;
}
else if (bit.find("hp") != std::string::npos)
{
fx.type = IET_HP;
}
else if (bit.find("li") != std::string::npos)
{
fx.type = IET_LI;
}
else if (bit.find("script") != std::string::npos)
{
fx.type = IET_SCRIPT;
}
int c = 0;
while (c < bit.size())
{
if (bit[c] == '+')
fx.magnitude += 1;
else if (bit[c] == '-')
fx.magnitude -= 1;
else if (bit[c] == '~')
fx.magnitude += 0.1f;
c++;
}
data->effects.push_back(fx);
}
}
}
ingredientData.push_back(data);
}
if(extradata)
{
while (std::getline(in, line))
{
SimpleIStringStream inLine(line.c_str(), SimpleIStringStream::REUSE);
int maxAmount = MAX_INGREDIENT_AMOUNT;
int rotKind = 1;
inLine >> name >> maxAmount >> rotKind;
if (name == "==Recipes==")
{
recipes = true;
break;
}
IngredientData *data = getIngredientDataByName(name);
if(!data)
{
errorLog("Specifying data for undefined ingredient: " + name);
continue;
}
data->maxAmount = maxAmount;
data->rotKind = rotKind;
}
}
if (recipes)
{
bool quitNext = false;
int index=0;
Recipe r;
while (in >> name)
{
if (name == "+")
{
continue;
}
else if (name == "=")
{
quitNext = true;
continue;
}
else
{
if (quitNext)
{
r.result = name;
r.resultDisplayName = getIngredientDisplayName(name);
}
else
{
IngredientType it = getIngredientTypeFromName(name);
if (it == IT_NONE)
{
r.addName(name);
}
else
{
r.addType(it, name);
}
}
}
if (quitNext)
{
r.index = index;
this->recipes.push_back(r);
r.clear();
quitNext = false;
index++;
}
}
}
in.close();
}
void Continuity::loadIngredientDisplayNames(const std::string& file)
{
InStream in(file);
if (!in)
return;
std::string line, name, text;
while (std::getline(in, line))
{
size_t pos = line.find(' ');
if (pos == std::string::npos)
continue;
name = line.substr(0, pos);
text = line.substr(pos + 1);
ingredientDisplayNames[name] = text;
}
}
void Continuity::learnFormUpgrade(FormUpgradeType form)
{
formUpgrades[form] = true;
}
bool Continuity::hasFormUpgrade(FormUpgradeType form)
{
return formUpgrades[form];
}
std::string Continuity::getInternalFormName()
{
switch(form)
{
case FORM_NORMAL:
return "normal";
case FORM_ENERGY:
return "energy";
case FORM_NATURE:
return "nature";
case FORM_BEAST:
return "beast";
case FORM_FISH:
return "fish";
case FORM_SPIRIT:
return "spirit";
case FORM_SUN:
return "sun";
case FORM_DUAL:
return "dual";
}
return "";
}
void Continuity::loadIntoSongBank(const std::string &file)
{
if(!exists(file))
return;
XMLDocument doc;
XMLError err = readXML(file, doc);
if(err == XML_ERROR_EMPTY_DOCUMENT)
return;
if(err != XML_SUCCESS)
{
errorLog("Failed to load song bank: Malformed XML");
return;
}
XMLElement *song = doc.FirstChildElement("Song");
while (song)
{
Song s;
if (song->Attribute("notes"))
{
std::string strng = song->Attribute("notes");
std::istringstream is(strng);
int note = 0;
while (is >> note)
{
s.notes.push_back(note);
}
}
if (song->Attribute("script"))
{
s.script = atoi(song->Attribute("script"));
}
int slot = -1;
if (song->Attribute("slot"))
{
slot = atoi(song->Attribute("slot"));
if (slot != -1)
{
if (song->Attribute("description"))
{
songSlotDescriptions[slot] = song->Attribute("description");
}
if (song->Attribute("vox"))
{
songSlotVox[slot] = song->Attribute("vox");
}
}
}
int idx = atoi(song->Attribute("idx"));
songBank[idx] = s;
if (slot != -1)
{
songSlotsToType[slot] = idx;
songTypesToSlot[idx] = slot;
}
if (song->Attribute("name"))
{
songSlotNames[slot] = song->Attribute("name");
}
song = song->NextSiblingElement("Song");
}
}
void Continuity::loadSongBank()
{
songSlotDescriptions.clear();
songSlotVox.clear();
songSlotsToType.clear();
songTypesToSlot.clear();
songSlotNames.clear();
songBank.clear();
loadIntoSongBank(localisePath("data/songs.xml"));
if (dsq->mod.isActive())
{
loadIntoSongBank(localisePath(dsq->mod.getPath() + "scripts/songs.xml", dsq->mod.getPath()));
}
}
int Continuity::getSongTypeBySlot(int slot)
{
return songSlotsToType[slot];
}
int Continuity::getSongSlotByType(int type)
{
return songTypesToSlot[type];
}
std::string Continuity::getDescriptionForSongSlot(int songSlot)
{
return songSlotDescriptions[songSlot];
}
std::string Continuity::getVoxForSongSlot(int songSlot)
{
return songSlotVox[songSlot];
}
EatData *Continuity::getEatData(const std::string &name)
{
for (int i = 0; i < eats.size(); i++)
{
if (eats[i].name == name)
return &eats[i];
}
return 0;
}
void Continuity::loadEatBank()
{
eats.clear();
std::string file;
bool found = false;
if (dsq->mod.isActive())
{
file = dsq->mod.getPath() + "eats.txt";
if(exists(file))
found = true;
}
if(!found)
file = "data/eats.txt";
InStream inf(file.c_str());
EatData curData;
std::string read;
while (inf >> read)
{
if (read.find(':')!=std::string::npos)
{
if (!curData.name.empty())
{
eats.push_back(curData);
debugLog("added eats: " + curData.name);
}
std::string name = read.substr(1, read.length());
EatData e;
curData = e;
curData.name = name;
}
else
{
if (!read.empty())
{
std::string eq, data;
inf >> eq;
std::getline(inf, data);
std::istringstream is(data);
if (read == "Shot")
{
is >> curData.shot;
}
else if (read == "AmmoUnitSize")
{
is >> curData.ammoUnitSize;
curData.ammo = curData.ammoUnitSize;
}
else if (read == "GetUnits")
{
is >> curData.getUnits;
}
else if (read == "Health")
{
is >> curData.health;
}
}
}
}
inf.close();
}
bool Continuity::hasLi()
{
return (getFlag(FLAG_LI) == 100);
}
std::string Continuity::getSongNameBySlot(int slot)
{
return songSlotNames[slot];
}
void Continuity::toggleLiCombat(bool t)
{
if (hasLi())
{
setFlag(FLAG_LICOMBAT, (int)t);
if (dsq->game->li)
{
dsq->game->li->message("c", 0);
}
}
}
void Continuity::warpLiToAvatar()
{
if (hasLi())
{
if (dsq->game && dsq->game->li && dsq->game->avatar)
dsq->game->li->position = dsq->game->avatar->position - Vector(0,-1);
}
}
Song *Continuity::getSongByIndex(int idx)
{
return &songBank[idx];
}
void Continuity::castSong(int num)
{
if (!dsq->continuity.hasSong((SongType)num)) return;
Entity *selected = dsq->game->avatar;
Song *song = getSongByIndex(num);
if (!song)
{
std::ostringstream os;
os << "Could not find song with index [" << num << "]";
debugLog(os.str());
}
float et = 0.5;
std::ostringstream os;
os << "Song/SongSlot-" << dsq->continuity.getSongSlotByType(num);
PauseQuad *effect = new PauseQuad();
effect->pauseLevel = 1;
effect->setTexture(os.str());
effect->position = selected->position + selected->offset;
effect->scale.interpolateTo(Vector(3,3), et);
effect->alpha.ensureData();
effect->alpha.data->path.addPathNode(0, 0);
effect->alpha.data->path.addPathNode(0.5, 0.1);
effect->alpha.data->path.addPathNode(1, 0.5);
effect->alpha.data->path.addPathNode(0, 0.9);
effect->alpha.data->path.addPathNode(0, 1);
effect->alpha.startPath(et);
effect->setLife(et+0.1f);
effect->setDecayRate(1);
effect->setPositionSnapTo(&dsq->game->avatar->position);
dsq->game->addRenderObject(effect, LR_PARTICLES);
// song->script == 0: internal handler only
// song->script == 1: script handler only
// song->script == 2: both
if (song->script)
{
if (dsq->mod.isActive())
dsq->runScriptNum(dsq->mod.getPath() + "scripts/songs.lua", "castSong", num);
else
dsq->runScriptNum("songs.lua", "castSong", num);
}
if (song->script != 1)
{
switch((SongType)num)
{
case SONG_SHIELDAURA:
dsq->game->avatar->doShieldSong();
break;
case SONG_BIND:
dsq->game->avatar->doBindSong();
break;
case SONG_ENERGYFORM:
dsq->game->avatar->changeForm(FORM_ENERGY);
break;
#ifndef AQUARIA_DEMO
case SONG_HEAL:
// do heal effects
sound->playSfx("Heal");
selected->heal(2);
selected->skeletalSprite.animate("healSelf", 0, 1);
break;
case SONG_TIME:
{
float v = 0.3;
dsq->gameSpeed.ensureData();
dsq->gameSpeed.data->path.clear();
dsq->gameSpeed.data->path.addPathNode(0,0);
dsq->gameSpeed.data->path.addPathNode(v,0.05);
dsq->gameSpeed.data->path.addPathNode(v,0.95);
dsq->gameSpeed.data->path.addPathNode(1,1.0);
dsq->gameSpeed.startPath(10);
}
break;
case SONG_LANCE:
{
Entity *e = dsq->game->getNearestEntity(dsq->game->avatar->position, 256, dsq->game->avatar, ET_ENEMY, DT_AVATAR_LANCEATTACH);
if (e)
{
e->attachLance();
}
}
break;
case SONG_LI:
if (!dsq->continuity.hasLi() && dsq->continuity.getFlag(FLAG_LI) > 100)
{
dsq->emote.playSfx(EMOTE_NAIJASADSIGH);
}
// HACK: when you first get li, the li pointer won't be set
if (dsq->game->li && dsq->game->avatar->isUnderWater() && dsq->continuity.hasLi())
{
if (!dsq->game->avatar->isNearObstruction(2) && !dsq->game->avatar->state.lockedToWall && !(dsq->game->li->position - dsq->game->avatar->position).isLength2DIn(400))
{
dsq->overlay->color = Vector(1,1,1);
dsq->fade(1, 0.3);
dsq->run(0.3);
warpLiToAvatar();
dsq->fade(0, 0.3);
dsq->run(0.3);
dsq->overlay->color = 0;
}
else if ((dsq->game->li->position - dsq->game->avatar->position).isLength2DIn(500))
{
if (dsq->continuity.getFlag(FLAG_LICOMBAT) == 1)
dsq->continuity.setFlag(FLAG_LICOMBAT, 0);
else
dsq->continuity.setFlag(FLAG_LICOMBAT, 1);
dsq->game->li->message("c", 0);
}
else
{
core->sound->playSfx("Denied");
}
}
else
{
core->sound->playSfx("Denied");
}
break;
case SONG_SPIRITFORM:
if (dsq->game->avatar->isUnderWater())
{
// Don't try to enter spirit form while warping,
// or we'll get stuck in the spirit world afterward.
bool inWarp = false;
const Vector avatarPosition(dsq->game->avatar->position);
for (Path *p = dsq->game->getFirstPathOfType(PATH_WARP); p; p = p->nextOfType)
{
if (p->isCoordinateInside(avatarPosition))
{
inWarp = true;
break;
}
}
if (inWarp)
core->sound->playSfx("SongFail");
else
dsq->game->avatar->changeForm(FORM_SPIRIT);
}
else
{
core->sound->playSfx("SongFail");
}
break;
case SONG_NATUREFORM:
dsq->game->avatar->changeForm(FORM_NATURE);
break;
case SONG_BEASTFORM:
dsq->game->avatar->changeForm(FORM_BEAST);
break;
case SONG_DUALFORM:
dsq->game->avatar->changeForm(FORM_DUAL);
break;
case SONG_SUNFORM:
dsq->game->avatar->changeForm(FORM_SUN);
break;
case SONG_FISHFORM:
dsq->game->avatar->changeForm(FORM_FISH);
break;
case SONG_SONGDOOR1:
case SONG_MAP:
break;
#endif
}
}
FOR_ENTITIES(i)
{
Entity *e = *i;
if ((e->position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
{
e->song((SongType)num);
}
}
for (int i = 0; i < dsq->game->getNumPaths(); i++)
{
Path *p = dsq->game->getPath(i);
if (p && !p->nodes.empty())
{
PathNode *n = &p->nodes[0];
if ((n->position - dsq->game->avatar->position).isLength2DIn(1000))
{
p->song((SongType)num);
}
}
}
}
void Continuity::setCostume(const std::string &c)
{
costume = c;
dsq->game->avatar->changeForm(FORM_NORMAL, false);
}
void Continuity::learnSong(int song)
{
knowsSong[song] = true;
}
void Continuity::unlearnSong(int song)
{
knowsSong[song] = false;
}
bool Continuity::isSongTypeForm(SongType s)
{
return (s == SONG_ENERGYFORM || s == SONG_BEASTFORM || s == SONG_NATUREFORM || s == SONG_SUNFORM || s == SONG_SPIRITFORM || s == SONG_FISHFORM || s== SONG_DUALFORM);
}
void Continuity::shortenSong(Song &song, int size)
{
if (song.notes.size() > size)
{
Song copy = song;
song.notes.clear();
for (int i = copy.notes.size()-size; i < copy.notes.size(); i++)
{
song.notes.push_back(copy.notes[i]);
}
}
}
struct SongCheck
{
SongCheck(int idx, Song *s)
{ rank = 0; pass = false; this->song = s; songIdx = idx; }
int songIdx;
int rank;
bool pass;
Song *song;
};
const int songTolerance = 4;
int Continuity::checkSongAssisted(const Song &s)
{
// shorten song
Song song = s;
shortenSong(song, 64);
std::vector<SongCheck> songChecks;
for (int c = 0; c < songBank.size(); c++)
{
int i = songSlotsToType[c];
if (knowsSong[i])
{
Song *s = &songBank[i];
songChecks.push_back(SongCheck(i, s));
}
}
for (int i = 0; i < songChecks.size(); i++)
{
int j=0,c=0,m=0,last=0,rank=0;
int ms=songChecks[i].song->notes.size();
j = 0;
loop:
rank = 0;
last = j;
m = 0;
for (c = 0; c < ms; c++)
{
while (j < song.notes.size() && (*songChecks[i].song).notes[c] != song.notes[j])
{
j++;
if (j >= song.notes.size())
break;
}
if (j < song.notes.size())
{
if (m == 0)
last = j-1;
int diff = j-last;
if (diff < 0)
diff = 1;
if (diff >= songTolerance)
break;
m++;
rank += diff;
last=j;
}
else
{
break;
}
}
if (m == ms)
{
// make sure last note is more or less close
if (song.notes.size()-last < 2)
{
songChecks[i].pass = true;
songChecks[i].rank = rank;
}
}
if (j < song.notes.size())
goto loop;
}
int songIdx = SONG_NONE, lowestRank = -1;
for (int i = 0; i < songChecks.size(); i++)
{
if (songChecks[i].pass)
{
int checkRank = songChecks[i].rank;
if (lowestRank == -1 || checkRank < lowestRank)
{
lowestRank = songChecks[i].rank;
songIdx = songChecks[i].songIdx;
}
}
}
return songIdx;
}
int Continuity::checkSong(const Song &song)
{
bool knowAllSongs = false;
// way too long song
if (song.notes.size() > 64) return SONG_NONE;
for (int c = 0; c < songBank.size(); c++)
{
int i = songSlotsToType[c];
if ((knowAllSongs || knowsSong[i]))
{
Song *s = &songBank[i];
if (s->notes.empty()) continue;
int j = 0;
{
bool foundSong = false;
int currentNote = 0;
for (j = 0; j < song.notes.size(); j++)
{
if (currentNote >= 0 && currentNote < (*s).notes.size())
{
int bankNote = (*s).notes[currentNote];
int songNote = song.notes[j];
if (bankNote == songNote)
{
currentNote++;
}
else
currentNote = 0;
if (currentNote == s->notes.size())
{
if (j == song.notes.size()-1)
{
foundSong = true;
break;
}
else
{
currentNote = 0;
}
}
}
}
if (j != song.notes.size()-1) foundSong = false;
if (foundSong)
{
return i;
}
}
}
}
return -1;
}
void Continuity::getHoursMinutesSeconds(int *hours, int *minutes, int *seconds)
{
(*hours) = int(this->seconds/(60*60));
(*minutes) = int((this->seconds/60) - ((*hours)*60));
(*seconds) = this->seconds - (*minutes)*60 - (*hours)*60*60;
}
bool Continuity::hasSong(int song)
{
return knowsSong[song];
}
std::string Continuity::getIngredientGfx(const std::string &name)
{
IngredientData *i = getIngredientDataByName(name);
if (i)
{
return i->gfx;
}
return "";
}
void Continuity::update(float dt)
{
if (dsq->game->isActive())
seconds += dt;
if (statsAndAchievements) {
statsAndAchievements->update(dt);
statsAndAchievements->RunFrame();
}
if (dsq->game->isActive() && !dsq->game->isPaused() )
{
if (liPowerTimer.updateCheck(dt))
{
liPower = 0;
}
if (speedMultTimer.updateCheck(dt))
{
speedMult = 1;
}
if (lightTimer.updateCheck(dt))
{
light = 0;
}
if (petPowerTimer.updateCheck(dt))
{
petPower = 0;
}
if (dsq->game->avatar && dsq->game->avatar->isInputEnabled())
{
if (poisonTimer.updateCheck(dt))
{
poison = 0;
}
if (poison)
{
if (poisonBitTimer.updateCheck(dt))
{
poisonBitTimer.start(poisonBitTimeAvatar);
if (dsq->game->avatar)
{
core->sound->playSfx("poison");
DamageData d;
d.damage = poison * 0.2f;
d.useTimer = 0;
d.damageType = DT_ENEMY_ACTIVEPOISON;
dsq->game->avatar->damage(d);
dsq->spawnParticleEffect("PoisonBubbles", dsq->game->avatar->position);
}
}
}
if (webTimer.updateCheck(dt))
{
dsq->game->avatar->clearWeb();
}
if (dsq->game->avatar->web)
{
if (webBitTimer.updateCheck(dt))
{
webBitTimer.start(webBitTime);
dsq->game->avatar->web->addPoint(dsq->game->avatar->position);
}
}
}
if (energyTimer.updateCheck(dt))
{
energyMult = 0;
}
if (defenseMultTimer.updateCheck(dt))
{
defenseMult = 1;
}
if (biteMultTimer.updateCheck(dt))
{
biteMult = 1;
}
if (fishPoisonTimer.updateCheck(dt))
{
fishPoison = 1;
}
if (tripTimer.updateCheck(dt))
{
dsq->game->avatar->removeTripEffects();
}
if (regenTimer.updateCheck(dt))
{
}
if (invincibleTimer.updateCheck(dt))
{
}
if (regenTimer.isActive())
{
{
Avatar *a = dsq->game->avatar;
if (a)
{
a->heal(dt*0.5f);
}
}
}
}
}
void Continuity::shiftWorlds()
{
WorldType lastWorld = worldType;
if (worldType == WT_NORMAL)
{
worldType = WT_SPIRIT;
dsq->game->setWorldPaused(true);
}
else if (worldType == WT_SPIRIT)
{
worldType = WT_NORMAL;
dsq->game->setWorldPaused(false);
}
FOR_ENTITIES(i)
{
Entity *e = *i;
e->shiftWorlds(lastWorld, worldType);
}
applyWorldEffects(worldType, 1, 1);
if (worldType == WT_SPIRIT)
core->sound->playSfx("Spirit-Enter");
else if (worldType == WT_NORMAL)
core->sound->playSfx("Spirit-Return");
}
BeaconData *Continuity::getBeaconByIndex(int index)
{
for (Beacons::iterator i = beacons.begin(); i != beacons.end(); i++)
{
if ((*i).index == index)
{
return &(*i); // stupidity
}
}
return 0;
}
void Continuity::setBeacon(int index, bool on, Vector pos, Vector color)
{
if (on)
{
BeaconData *b = getBeaconByIndex(index);
if (!b)
{
BeaconData newb;
newb.index = index;
beacons.push_back(newb);
b = getBeaconByIndex(index);
}
b->on = true;
b->pos = pos;
b->color = color;
}
else
{
BeaconData *b = getBeaconByIndex(index);
if (b)
{
b->on = false;
}
}
}
void Continuity::applyWorldEffects(WorldType type, bool transition, bool affectMusic)
{
float time = 1;
if (!transition) time = 0;
if (type == WT_SPIRIT)
{
if(dsq->user.video.blur)
{
core->postProcessingFx.blendType = 1;
core->postProcessingFx.intensity = 0.2f;
core->postProcessingFx.layer = LR_AFTER_EFFECTS;
core->postProcessingFx.renderLayer = LR_AFTER_EFFECTS;
core->postProcessingFx.enable(FXT_RADIALBLUR);
}
dsq->game->avatar->canWarp = false;
dsq->game->sceneColor.interpolateTo(Vector(0.4, 0.8, 0.9), time);
dsq->game->avatar->applyWorldEffects(type);
}
else
{
dsq->game->avatar->canWarp = true;
core->postProcessingFx.disable(FXT_RADIALBLUR);
dsq->game->sceneColor.interpolateTo(Vector(1,1,1), time);
dsq->game->avatar->applyWorldEffects(type);
}
if (time > 0)
{
}
worldType = type;
}
void Continuity::eatBeast(const EatData &eatData)
{
if (!eatData.name.empty())
{
for (int i = 0; i < eatData.getUnits; i++)
{
if (naijaEats.size() < MAX_EATS)
{
if (!eatData.shot.empty())
naijaEats.push_back(eatData);
}
}
if (eatData.health > 0)
{
dsq->game->avatar->heal(eatData.health);
}
}
}
void Continuity::removeNaijaEat(int idx)
{
std::vector<EatData> copy = naijaEats;
naijaEats.clear();
for (int i = 0; i < copy.size(); i++)
{
if (i != idx)
naijaEats.push_back(copy[i]);
}
}
void Continuity::removeLastNaijaEat()
{
removeNaijaEat(naijaEats.size()-1);
}
EatData *Continuity::getLastNaijaEat()
{
if (naijaEats.empty())
return 0;
return &naijaEats[naijaEats.size()-1];
}
bool Continuity::isNaijaEatsEmpty()
{
return naijaEats.empty();
}
void Continuity::init()
{
statsAndAchievements = new StatsAndAchievements;
}
void Continuity::shutdown()
{
if (statsAndAchievements)
{
delete statsAndAchievements;
statsAndAchievements = 0;
}
}
void Continuity::initAvatar(Avatar *a)
{
debugLog("in initAvatar");
a->maxHealth = maxHealth;
a->health = 0;
a->heal(health);
// block spirit form in case of bug
if (form == FORM_SPIRIT)
form = FORM_NORMAL;
debugLog("changeForm...");
a->changeForm(form, false, true, FORM_NORMAL);
debugLog("done");
debugLog("auraType...");
if (auraType != AURA_NONE && auraTimer > 0)
{
a->activateAura(auraType);
a->auraTimer = auraTimer;
}
debugLog("trip");
if (tripTimer.isActive())
{
a->applyTripEffects();
}
debugLog("web");
if (webTimer.isActive())
{
setWeb(webTimer.getValue());
}
debugLog("done initAvatar");
// HACK-ish
a->skeletalSprite.stopAllAnimations();
a->skeletalSprite.animate(a->getIdleAnimName(), -1, 0);
}
void Continuity::spawnAllIngredients(const Vector &position)
{
for (int i = 0; i < ingredientData.size(); i++)
{
dsq->game->spawnIngredient(ingredientData[i]->name, position, 4, 0);
}
}
void Continuity::removeEmptyIngredients()
{
for (IngredientDatas::iterator i = ingredients.begin(); i != ingredients.end();)
{
IngredientData *data = *i;
if (data->amount == 0 && data->held <= 0)
{
i = ingredients.erase(i);
}
else
{
++ i;
}
}
}
void Continuity::refreshAvatarData(Avatar *a)
{
maxHealth = a->maxHealth;
health = a->health;
auraType = a->activeAura;
auraTimer = a->auraTimer;
}
int Continuity::getFlag(std::string flag)
{
if (flag == "story")
errorLog("Hey! Use the new fancy story functions!");
return flags[flag];
}
void Continuity::setFlag(std::string flag, int v)
{
flags[flag] = v;
}
void Continuity::loadPetData()
{
petData.clear();
InStream in("data/pets.txt");
std::string read;
while (std::getline(in, read))
{
int num=0;
PetData p;
std::istringstream is(read);
is >> num >> p.namePart;
petData.push_back(p);
}
in.close();
}
PetData *Continuity::getPetData(int idx)
{
if (idx < 0 || idx >= petData.size())
{
std::ostringstream os;
os << "getPetData(" << idx << ") index out of range";
debugLog(os.str());
return 0;
}
return &petData[idx];
}
bool Continuity::isStory(float v)
{
return (story == v);
}
float Continuity::getStory()
{
return story;
}
void Continuity::setStory(float v)
{
story = v;
}
std::string Continuity::getStringFlag(std::string flag)
{
return stringFlags[flag];
}
void Continuity::setStringFlag(std::string flag, std::string v)
{
stringFlags[flag] = v;
}
void Continuity::clearTempFlags()
{
for (Flags::iterator i = flags.begin(); i != flags.end(); i++)
{
if ((*i).first.find("CHOICE_")!=std::string::npos)
{
(*i).second = 0;
}
}
}
void Continuity::upgradeHealth()
{
Avatar *a = dsq->game->avatar;
maxHealth = a->maxHealth+1;
a->maxHealth = maxHealth;
a->heal(maxHealth - a->health);
}
void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, int scrShotWidth, int scrShotHeight)
{
refreshAvatarData(dsq->game->avatar);
if (position.isZero())
{
position = dsq->game->avatar->position;
}
dsq->user.save();
XMLDocument doc;
XMLElement *version = doc.NewElement("Version");
{
version->SetAttribute("major", VERSION_MAJOR);
version->SetAttribute("minor", VERSION_MINOR);
version->SetAttribute("revision", VERSION_REVISION);
}
doc.InsertEndChild(version);
for (Flags::iterator i = flags.begin(); i != flags.end(); i++)
{
if ((*i).first.find("CHOICE_")!=std::string::npos) continue;
if ((*i).first.find("TEMP_")!=std::string::npos) continue;
XMLElement *flag = doc.NewElement("Flag");
flag->SetAttribute("name", (*i).first.c_str());
flag->SetAttribute("value", (*i).second);
doc.InsertEndChild(flag);
}
XMLElement *efx = doc.NewElement("EFX");
{
std::ostringstream os;
for (EntityFlags::iterator i = entityFlags.begin(); i != entityFlags.end(); i++)
{
os << (*i).first << " " << (*i).second << " ";
}
efx->SetAttribute("a", os.str().c_str());
}
doc.InsertEndChild(efx);
XMLElement *gems = doc.NewElement("Gems");
{
std::ostringstream os;
bool hasUserString = false;
os << this->gems.size() << " ";
for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++)
{
os << (*i).name << " " << (*i).pos.x << " " << (*i).pos.y << " ";
os << (*i).canMove << " ";
hasUserString = !(*i).userString.empty();
os << hasUserString << " ";
if (hasUserString)
os << spacesToUnderscores((*i).userString) << " ";
}
gems->SetAttribute("c", os.str().c_str());
// This is the format used in the android version. Keeping this commented out for now,
// but it should be used instead of the code above in some time -- FG
/*
os.str("");
bool hasMapName = false;
os << this->gems.size() << " ";
for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++)
{
os << (*i).name << " ";
hasMapName = !(*i).mapName.empty();
os << hasMapName << " ";
if(hasMapName)
os << (*i).mapName << " "; // warning: this will fail to load if the map name contains whitespace
os << (*i).pos.x << " " << (*i).pos.y << " ";
os << (*i).canMove << " ";
hasUserString = !(*i).userString.empty();
os << hasUserString << " ";
if (hasUserString)
os << spacesToUnderscores((*i).userString) << " ";
}
gems->SetAttribute("d", os.str());
*/
}
doc.InsertEndChild(gems);
XMLElement *worldMap = doc.NewElement("WorldMap");
{
std::ostringstream os;
for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile->revealed)
{
os << tile->index << " ";
}
}
worldMap->SetAttribute("b", os.str().c_str());
if (dsq->game->worldMapRender)
{
std::ostringstream os;
for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
os << tile->index << " ";
tile->dataToString(os);
os << " ";
}
worldMap->SetAttribute("va", os.str().c_str());
}
}
doc.InsertEndChild(worldMap);
XMLElement *vox = doc.NewElement("VO");
{
std::ostringstream os;
for (int i = 0; i < dsq->continuity.voiceOversPlayed.size(); i++)
{
os << dsq->continuity.voiceOversPlayed[i] << " ";
}
vox->SetAttribute("v", os.str().c_str());
}
doc.InsertEndChild(vox);
XMLElement *eats = doc.NewElement("eats");
{
std::ostringstream os;
int num = naijaEats.size();
os << num << " ";
for (int i = 0; i < num; i++)
{
EatData *eat = &naijaEats[i];
os << eat->name << " " << eat->shot << " " << eat->ammo << " " << eat->ammoUnitSize << " ";
}
eats->SetAttribute("a", os.str().c_str());
}
doc.InsertEndChild(eats);
XMLElement *bcn = doc.NewElement("bcn");
{
std::ostringstream os;
for (Beacons::iterator i = beacons.begin(); i != beacons.end(); i++)
{
BeaconData *data = &(*i);
os << data->index << " " << data->on << " ";
os << data->color.x << " " << data->color.y << " " << data->color.z << " ";
os << data->pos.x << " " << data->pos.y << " " << data->pos.z << " ";
}
bcn->SetAttribute("a", os.str().c_str());
}
doc.InsertEndChild(bcn);
XMLElement *s = doc.NewElement("Story");
{
std::ostringstream os;
os << story;
s->SetAttribute("v", os.str().c_str());
doc.InsertEndChild(s);
}
for (StringFlags::iterator i = stringFlags.begin(); i != stringFlags.end(); i++)
{
if ((*i).first.find("TEMP_")!=std::string::npos) continue;
XMLElement *stringFlag = doc.NewElement("StringFlag");
stringFlag->SetAttribute("name", (*i).first.c_str());
stringFlag->SetAttribute("value", (*i).second.c_str());
doc.InsertEndChild(stringFlag);
}
XMLElement *startData = doc.NewElement("StartData");
startData->SetAttribute("x", int(position.x));
startData->SetAttribute("y", int(position.y));
startData->SetAttribute("scene", dsq->game->sceneName.c_str());
startData->SetAttribute("sceneDisplayName", dsq->game->sceneDisplayName.c_str());
startData->SetAttribute("exp", dsq->continuity.exp);
startData->SetAttribute("h", dsq->continuity.maxHealth);
startData->SetAttribute("ch", dsq->continuity.health);
startData->SetAttribute("naijaModel", dsq->continuity.naijaModel.c_str());
startData->SetAttribute("costume", dsq->continuity.costume.c_str());
startData->SetAttribute("form", dsq->continuity.form);
if (dsq->mod.isActive())
startData->SetAttribute("mod", dsq->mod.getName().c_str());
std::ostringstream secondsOS;
secondsOS << dsq->continuity.seconds;
startData->SetAttribute("seconds", secondsOS.str().c_str());
std::ostringstream os2;
for (int i = 0; i < SONG_MAX; i++)
{
if (knowsSong[i])
{
os2 << i << " ";
}
}
startData->SetAttribute("songs", os2.str().c_str());
// new format as used by android version
std::ostringstream ingrNames;
for (int i = 0; i < ingredients.size(); i++)
{
IngredientData *data = ingredients[i];
ingrNames << data->name << " " << data->amount << " ";
}
startData->SetAttribute("ingrNames", ingrNames.str().c_str());
// for compatibility with older versions
std::ostringstream ingrOs;
for (int i = 0; i < ingredients.size(); i++)
{
IngredientData *data = ingredients[i];
ingrOs << data->getIndex() << " " << data->amount << " ";
}
startData->SetAttribute("ingr", ingrOs.str().c_str());
std::ostringstream recOs;
for (int i = 0; i < recipes.size(); i++)
{
recOs << recipes[i].isKnown() << " ";
}
startData->SetAttribute("rec", recOs.str().c_str());
std::ostringstream os3;
for (int i = 0; i < FORMUPGRADE_MAX; i++)
{
if (hasFormUpgrade((FormUpgradeType)i))
{
os3 << i << " ";
}
}
startData->SetAttribute("formUpgrades", os3.str().c_str());
std::ostringstream fos;
fos << MAX_FLAGS << " ";
for (int i = 0; i < MAX_FLAGS; i++)
{
fos << intFlags[i] << " ";
}
startData->SetAttribute("intFlags", fos.str().c_str());
// Additional data for the android version
#define SINGLE_FLOAT_ATTR(name, cond, val) \
do { if((cond) && (val)) { \
std::ostringstream osf; \
osf << (val); \
startData->SetAttribute(name, osf.str().c_str()); \
}} while(0)
SINGLE_FLOAT_ATTR("blind", dsq->game->avatar->state.blind, dsq->game->avatar->state.blindTimer.getValue());
SINGLE_FLOAT_ATTR("invincible", invincibleTimer.isActive(), invincibleTimer.getValue());
SINGLE_FLOAT_ATTR("regen", regenTimer.isActive(), regenTimer.getValue());
SINGLE_FLOAT_ATTR("trip", tripTimer.isActive(), tripTimer.getValue());
SINGLE_FLOAT_ATTR("shieldPoints", true, dsq->game->avatar->shieldPoints);
SINGLE_FLOAT_ATTR("webTimer", webTimer.isActive(), webTimer.getValue()); // Extension; not present in the android version
#undef SINGLE_FLOAT_ATTR
#define TIMER_AND_VALUE_ATTR(name, timer, val) \
do { if(((timer).isActive()) && (val)) { \
std::ostringstream osf; \
osf << (val) << " " << ((timer).getValue()); \
startData->SetAttribute((name), osf.str().c_str()); \
}} while(0)
TIMER_AND_VALUE_ATTR("biteMult", biteMultTimer, biteMult);
TIMER_AND_VALUE_ATTR("speedMult", speedMultTimer, speedMult);
TIMER_AND_VALUE_ATTR("defenseMult", defenseMultTimer, defenseMult);
TIMER_AND_VALUE_ATTR("energyMult", energyTimer, energyMult);
TIMER_AND_VALUE_ATTR("petPower", petPowerTimer, petPower);
TIMER_AND_VALUE_ATTR("liPower", liPowerTimer, liPower);
TIMER_AND_VALUE_ATTR("light", lightTimer, light);
#undef TIMER_AND_VALUE_ATTR
if(poisonTimer.isActive())
{
std::ostringstream osp;
osp << poison << " " << poisonTimer.getValue() << " " << poisonBitTimer.getValue();
startData->SetAttribute("poison", osp.str().c_str());
}
if(dsq->game->avatar->activeAura != AURA_NONE)
{
std::ostringstream osa;
osa << dsq->game->avatar->activeAura << " " << dsq->game->avatar->auraTimer;
startData->SetAttribute("aura", osa.str().c_str());
}
// FIXME: Web is a bit weird. There are 2 webBitTimer variables in use, one in Continuity, one in Avatar.
// Because the avatar one ticks every 0.5 seconds, it will be hardly noticeable if that timer is off.
// So we just use the Continuty timers and hope for the best. -- FG
if(webTimer.isActive() && dsq->game->avatar->web)
{
Web *w = dsq->game->avatar->web;
const int nump = w->getNumPoints();
std::ostringstream osw;
osw << webBitTimer.getValue() << " " << nump << " ";
for(int i = 0; i < nump; ++i)
{
Vector v = w->getPoint(i);
osw << v.x << " " << v.y << " ";
}
startData->SetAttribute("web", osw.str().c_str());
}
// end extra android data
doc.InsertEndChild(startData);
std::string fn = adjustFilenameCase(getSaveFileName(slot, "aqs"));
FILE *fh = fopen(fn.c_str(), "wb");
if(!fh)
{
debugLog("FAILED TO SAVE GAME");
return;
}
XMLPrinter printer;
doc.Accept( &printer );
const char* xmlstr = printer.CStr();
ZlibCompressor z;
z.init((void*)xmlstr, printer.CStrSize(), ZlibCompressor::REUSE);
z.SetForceCompression(true);
z.Compress(3);
std::ostringstream os;
os << "Writing " << z.size() << " bytes to save file " << fn;
debugLog(os.str());
size_t written = fwrite(z.contents(), 1, z.size(), fh);
if (written != z.size())
{
debugLog("FAILED TO WRITE SAVE FILE COMPLETELY");
}
fclose(fh);
}
std::string Continuity::getSaveFileName(int slot, const std::string &pfix)
{
std::ostringstream os;
os << dsq->getSaveDirectory() << "/" << dsq->currentProfile.name << "-" << numToZeroString(slot, 4) << "." << pfix;
return os.str();
}
void Continuity::loadFileData(int slot, XMLDocument &doc)
{
std::string teh_file = dsq->continuity.getSaveFileName(slot, "aqs");
if(!exists(teh_file))
teh_file = dsq->continuity.getSaveFileName(slot, "bin");
if (exists(teh_file))
{
unsigned long size = 0;
char *buf = readCompressedFile(teh_file, &size);
if (!buf)
{
errorLog("Failed to decompress save file: " + teh_file);
return;
}
if (doc.Parse(buf, size) != XML_SUCCESS)
{
errorLog("Failed to load save data: " + teh_file + " -- Error: " + doc.GetErrorStr1());
return;
}
}
teh_file = dsq->continuity.getSaveFileName(slot, "xml");
if (exists(teh_file))
{
if (readXML(teh_file, doc) != XML_SUCCESS)
errorLog("Failed to load save data: " + teh_file);
}
}
void Continuity::loadFile(int slot)
{
dsq->user.save();
XMLDocument doc;
loadFileData(slot, doc);
XMLElement *startData = doc.FirstChildElement("StartData");
if (startData)
{
if (startData->Attribute("mod"))
{
#ifdef AQUARIA_DEMO
exit_error("The demo version does not support loading savegames from mods, sorry.");
#else
dsq->mod.load(startData->Attribute("mod"));
#endif
}
}
this->reset();
int versionMajor=-1, versionMinor=-1, versionRevision=-1;
XMLElement *xmlVersion = doc.FirstChildElement("Version");
if (xmlVersion)
{
versionMajor = atoi(xmlVersion->Attribute("major"));
versionMinor = atoi(xmlVersion->Attribute("minor"));
versionRevision = atoi(xmlVersion->Attribute("revision"));
}
XMLElement *e = doc.FirstChildElement("Flag");
while (e)
{
dsq->continuity.setFlag(e->Attribute("name"), atoi(e->Attribute("value")));
e = e->NextSiblingElement("Flag");
}
XMLElement *efx = doc.FirstChildElement("EFX");
if (efx)
{
if (efx->Attribute("a"))
{
std::istringstream is(efx->Attribute("a"));
std::string name;
while (is >> name)
{
is >> entityFlags[name];
}
}
}
XMLElement *eats = doc.FirstChildElement("eats");
if (eats)
{
if (eats->Attribute("a"))
{
std::istringstream is(eats->Attribute("a"));
int num = 0;
naijaEats.clear();
is >> num;
for (int i = 0; i < num; i++)
{
EatData eat;
is >> eat.name >> eat.shot >> eat.ammo >> eat.ammoUnitSize;
naijaEats.push_back(eat);
}
}
}
XMLElement *bcn = doc.FirstChildElement("bcn");
if (bcn)
{
if (bcn->Attribute("a"))
{
beacons.clear();
std::istringstream is(bcn->Attribute("a"));
int idx=0;
while (is >> idx)
{
BeaconData data;
data.index = idx;
is >> data.on;
is >> data.color.x >> data.color.y >> data.color.z;
is >> data.pos.x >> data.pos.y >> data.pos.z;
beacons.push_back(data);
}
}
}
XMLElement *vox = doc.FirstChildElement("VO");
if (vox)
{
std::string s = vox->Attribute("v");
std::istringstream is(s);
std::string v;
while (is >> v)
{
dsq->continuity.voiceOversPlayed.push_back(v);
}
}
XMLElement *gems = doc.FirstChildElement("Gems");
if (gems)
{
if (gems->Attribute("a"))
{
std::string s = gems->Attribute("a");
std::istringstream is(s);
GemData g;
while (is >> g.name)
{
is >> g.pos.x >> g.pos.y;
this->gems.push_back(g);
}
}
else if (gems->Attribute("b"))
{
std::string s = gems->Attribute("b");
std::istringstream is(s);
GemData g;
bool hasUserString = false;
while (is >> g.name)
{
hasUserString=false;
is >> g.pos.x >> g.pos.y;
is >> g.canMove;
is >> hasUserString;
if (hasUserString)
is >> g.userString;
std::ostringstream os;
os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
debugLog(os.str());
g.userString = underscoresToSpaces(g.userString);
this->gems.push_back(g);
}
}
// num [name mapX mapY canMove hasUserString (userString)]
else if (gems->Attribute("c"))
{
std::string s = gems->Attribute("c");
std::istringstream is(s);
int num = 0;
is >> num;
bool hasUserString = false;
GemData g;
std::ostringstream os;
os << "continuity num: [" << num << "]" << std::endl;
os << "data: [" << s << "]" << std::endl;
debugLog(os.str());
for (int i = 0; i < num; i++)
{
g.pos = Vector(0,0,0);
g.canMove = false;
g.userString = "";
hasUserString=false;
is >> g.name;
is >> g.pos.x >> g.pos.y;
is >> g.canMove;
is >> hasUserString;
if (hasUserString)
is >> g.userString;
else
g.userString = "";
g.userString = underscoresToSpaces(g.userString);
this->gems.push_back(g);
std::ostringstream os;
os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
debugLog(os.str());
}
}
// num [name hasMapName (mapName) mapX mapY canMove hasUserString (userString)]
else if (gems->Attribute("d"))
{
std::string s = gems->Attribute("d");
std::istringstream is(s);
int num = 0;
is >> num;
bool hasUserString = false;
bool hasMapName = false;
GemData g;
std::ostringstream os;
os << "continuity num: [" << num << "]" << std::endl;
os << "data: [" << s << "]" << std::endl;
debugLog(os.str());
for (int i = 0; i < num; i++)
{
g.pos = Vector(0,0,0);
g.canMove = false;
g.userString = "";
g.mapName = "";
hasUserString=false;
hasMapName = false;
is >> g.name;
is >> hasMapName;
if(hasMapName)
is >> g.mapName;
is >> g.pos.x >> g.pos.y;
is >> g.canMove;
is >> hasUserString;
if (hasUserString)
is >> g.userString;
g.userString = underscoresToSpaces(g.userString);
this->gems.push_back(g);
std::ostringstream os;
os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
debugLog(os.str());
}
}
}
XMLElement *worldMap = doc.FirstChildElement("WorldMap");
if (worldMap)
{
if (worldMap->Attribute("b"))
{
std::string s = worldMap->Attribute("b");
std::istringstream is(s);
int idx;
while (is >> idx)
{
dsq->continuity.worldMap.revealMapIndex(idx);
}
}
if (worldMap->Attribute("va") && dsq->continuity.worldMap.getNumWorldMapTiles())
{
std::istringstream is(worldMap->Attribute("va"));
WorldMapTile dummy;
int idx;
while (is >> idx)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(idx);
if (!tile)
{
std::ostringstream os;
os << "tile dummy: dropping data for worldmap tile index " << idx;
debugLog(os.str());
tile = &dummy;
}
tile->stringToData(is);
}
}
}
XMLElement *s = doc.FirstChildElement("Story");
if (s)
{
std::istringstream is(s->Attribute("v"));
is >> story;
}
XMLElement *e2 = doc.FirstChildElement("StringFlag");
while (e2)
{
dsq->continuity.setStringFlag(e2->Attribute("name"), e2->Attribute("value"));
e2 = e2->NextSiblingElement("StringFlag");
}
if (startData)
{
int x = atoi(startData->Attribute("x"));
int y = atoi(startData->Attribute("y"));
dsq->game->positionToAvatar = Vector(x,y);
if (startData->Attribute("exp"))
exp = atoi(startData->Attribute("exp"));
if (startData->Attribute("naijaModel"))
{
//dsq->continuity.naijaModel = startData->Attribute("naijaModel");
}
if (startData->Attribute("form"))
{
dsq->continuity.form = FormType(atoi(startData->Attribute("form")));
}
if (startData->Attribute("ingrNames"))
{
std::istringstream is(startData->Attribute("ingrNames"));
std::string name;
while (is >> name)
{
int amount=0;
is >> amount;
IngredientData *data = getIngredientDataByName(name);
if (data)
{
data->amount = 0;
pickupIngredient(data, amount, false);
}
}
}
else if (startData->Attribute("ingr")) // use this only if ingrNames does not exist.
{
std::istringstream is(startData->Attribute("ingr"));
int idx;
while (is >> idx)
{
int amount=0;
is >> amount;
IngredientData *data = getIngredientDataByIndex(idx);
if (data)
{
data->amount = 0;
pickupIngredient(data, amount, false);
}
}
}
if (startData->Attribute("rec"))
{
std::istringstream is(startData->Attribute("rec"));
for (int i = 0; i < recipes.size(); i++)
{
bool known = false;
is >> known;
if (known)
recipes[i].learn();
}
}
if (startData->Attribute("songs"))
{
std::istringstream is(std::string(startData->Attribute("songs")));
int v=0;
while (is >> v)
{
knowsSong[v] = true;
}
}
if (startData->Attribute("formUpgrades"))
{
std::istringstream is(std::string(startData->Attribute("formUpgrades")));
int v = 0;
while (is >> v)
{
learnFormUpgrade(FormUpgradeType(v));
}
}
if (startData->Attribute("intFlags"))
{
std::istringstream is(std::string(startData->Attribute("intFlags")));
int numFlags;
is >> numFlags;
if (numFlags > MAX_FLAGS)
numFlags = MAX_FLAGS;
for (int i = 0; i < numFlags; i++)
{
is >> intFlags[i];
}
}
if (startData->Attribute("h"))
{
float read = strtof(startData->Attribute("h"), NULL);
maxHealth = read;
health = read;
std::ostringstream os;
os << "MaxHealth read as: " << maxHealth;
debugLog(os.str());
if (dsq->game->avatar)
{
dsq->game->avatar->maxHealth = maxHealth;
dsq->game->avatar->health = maxHealth;
}
}
if (startData->Attribute("ch"))
{
float h = strtof(startData->Attribute("ch"), NULL);
health = h;
std::ostringstream os;
os << "CurHealth read as: " << health;
debugLog(os.str());
if (dsq->game->avatar)
{
dsq->game->avatar->health = h;
}
}
if (startData->Attribute("seconds"))
{
std::istringstream is(startData->Attribute("seconds"));
is >> dsq->continuity.seconds;
}
if (startData->Attribute("costume"))
{
dsq->continuity.costume = startData->Attribute("costume");
}
dsq->game->sceneToLoad = startData->Attribute("scene");
// Additional data introduced in the android version
if(startData->Attribute("blind"))
{
float timer = strtof(startData->Attribute("blind"), NULL);
if(dsq->game->avatar)
dsq->game->avatar->setBlind(timer);
}
if(startData->Attribute("invincible"))
{
float timer = strtof(startData->Attribute("invincible"), NULL);
setInvincible(timer);
}
if(startData->Attribute("regen"))
{
float timer = strtof(startData->Attribute("regen"), NULL);
setRegen(timer);
}
if(startData->Attribute("trip"))
{
float timer = strtof(startData->Attribute("trip"), NULL);
setTrip(timer);
}
if(startData->Attribute("aura"))
{
std::istringstream is(startData->Attribute("aura"));
int type = AURA_NONE;
float timer = 0.0f;
is >> type >> timer;
auraTimer = timer;
auraType = (AuraType)type;
if(dsq->game->avatar)
{
dsq->game->avatar->activateAura((AuraType)type);
dsq->game->avatar->auraTimer = timer;
}
}
if(startData->Attribute("shieldPoints"))
{
float sp = strtof(startData->Attribute("shieldPoints"), NULL);
if(dsq->game->avatar)
dsq->game->avatar->shieldPoints = sp;
}
#define LOAD_MULTI_SIMPLE(attr, mth) \
do { if(startData->Attribute(attr)) \
{ \
std::istringstream is(startData->Attribute(attr)); \
float value = 0.0f, timer = 0.0f; \
is >> value >> timer; \
this->mth(value, timer); \
}} while(0)
LOAD_MULTI_SIMPLE("biteMult", setBiteMultiplier);
LOAD_MULTI_SIMPLE("speedMult", setSpeedMultiplier);
LOAD_MULTI_SIMPLE("defenseMult", setDefenseMultiplier);
LOAD_MULTI_SIMPLE("energyMult", setEnergy);
LOAD_MULTI_SIMPLE("petPower", setPetPower);
LOAD_MULTI_SIMPLE("liPower", setLiPower);
LOAD_MULTI_SIMPLE("light", setLight);
#undef LOAD_MULTI_SIMPLE
if(startData->Attribute("poison"))
{
std::istringstream is(startData->Attribute("poison"));
float p = 0.0f, pt = 0.0f, pbit = 0.0f;
is >> p >> pt >> pbit;
setPoison(p, pt);
poisonBitTimer.start(pbit);
}
// FIXME: the total web time is seemingly not saved in the file.
// Not sure if the calculation of the remaining time is correct.
// Especially because there are two webBitTimer variables in use (in Continuity and Avatar),
// and both of them access the avatar web. It's thus likely that more points were added than intended. -- FG
if(startData->Attribute("web"))
{
std::istringstream is(startData->Attribute("web"));
float wbit = 0.0f;
int nump = 0;
is >> wbit >> nump;
// 2 web points are added in setWeb() by default, so we exclude them from the calculation
float remainTime = webTime - (0.5 * (nump - 2)); // Avatar::webBitTimer ticks every 0.5 secs
if(nump > 1 && remainTime > 0 && dsq->game->avatar)
{
if(!dsq->game->avatar->web)
dsq->game->avatar->createWeb();
Web *w = dsq->game->avatar->web;
for(int i = 0; i < nump; ++i)
{
Vector v;
is >> v.x >> v.y;
if(i < w->getNumPoints())
w->setPoint(i, v);
else
w->addPoint(v);
}
webBitTimer.start(wbit);
webTimer.start(remainTime);
}
}
// This is AFAIK not in the android version, but let's add this for completeness
// and to avoid the mess described above.
if(startData->Attribute("webTimer"))
{
float timer = strtof(startData->Attribute("webTimer"), NULL);
webTimer.start(timer);
}
}
}
void Continuity::setNaijaModel(std::string model)
{
}
int Continuity::getFlag(int flag)
{
if (flag == 0)
{
errorLog("Flag 0 not allowed");
}
return intFlags[flag];
}
void Continuity::setFlag(int flag, int v)
{
if (flag == 0)
{
errorLog("Flag 0 not allowed");
}
std::ostringstream os;
os << "setting flag [" << flag << "] to " << v;
debugLog(os.str());
intFlags[flag] = v;
}
int Continuity::getEntityFlag(const std::string &sceneName, int id)
{
std::ostringstream os;
os << sceneName << ":" << id;
std::ostringstream os2;
os2 << hash(os.str());
return entityFlags[os2.str()];
}
void Continuity::setEntityFlag(const std::string &sceneName, int id, int v)
{
std::ostringstream os;
os << sceneName << ":" << id;
std::ostringstream os2;
os2 << hash(os.str());
entityFlags[os2.str()] = v;
}
void Continuity::setPathFlag(Path *p, int v)
{
std::ostringstream os;
os << "p:" << dsq->game->sceneName << ":" << p->nodes[0].position.x << ":" << p->nodes[0].position.y << ":" << removeSpaces(p->name);
std::ostringstream os2;
os2 << hash(os.str());
entityFlags[os2.str()] = v;
}
int Continuity::getPathFlag(Path *p)
{
std::ostringstream os;
os << "p:" << dsq->game->sceneName << ":" << p->nodes[0].position.x << ":" << p->nodes[0].position.y << ":" << removeSpaces(p->name);
std::ostringstream os2;
os2 << hash(os.str());
return entityFlags[os2.str()];
}
class GemGet : public Quad
{
public:
GemGet(const std::string &gem)
{
timeScale = 3;
timer = 0;
setTexture("Gems/" + gem);
followCamera = 1;
scale = Vector(0, 0);
scale.ensureData();
scale.data->path.addPathNode(Vector(0,0), 0);
scale.data->path.addPathNode(Vector(1,1), 0.3);
scale.data->path.addPathNode(Vector(1,1), 0.6);
scale.data->path.addPathNode(Vector(0.5,0.5), 0.9);
scale.data->path.addPathNode(Vector(0.1,0.1), 1);
scale.startPath(timeScale);
position = Vector(400,400);
setLife(timeScale+0.1f);
setDecayRate(1);
}
protected:
float timer;
float timeScale;
Vector startPos;
void onUpdate(float dt)
{
Quad::onUpdate(dt);
timer += dt;
if (timer > 0.6f*timeScale)
{
if (startPos.isZero())
{
startPos = position;
}
else
{
float p = (timer - (0.6f*timeScale)) / (0.4f*timeScale);
position = ((dsq->game->miniMapRender->position + dsq->game->miniMapRender->offset) - startPos)*p + startPos;
}
}
}
};
// WARNING: invalidates pointers to gems!
// BUT: maybe not
void Continuity::removeGemData(GemData *gemData)
{
for (Gems::iterator i = gems.begin(); i != gems.end(); i++)
{
if (&(*i) == gemData)
{
gems.erase(i);
return; // safety first
}
}
}
GemData *Continuity::pickupGem(std::string name, bool effects)
{
GemData g;
g.name = name;
g.mapName = dsq->game->sceneName;
int sz = gems.size();
//HACK: (hacky) using effects to determine the starting position of the gem
if (!effects)
{
g.pos = dsq->game->worldMapRender->getAvatarWorldMapPosition() + Vector(sz*16-64, -64);
}
else
{
if (!gems.empty())
g.pos = dsq->game->worldMapRender->getAvatarWorldMapPosition();
else
g.pos = Vector(0,0);
}
gems.push_back(g);
if (effects && dsq->game->isActive())
{
core->sound->playSfx("Gem-Collect");
GemGet *gg = new GemGet(g.name);
dsq->game->addRenderObject(gg, LR_MINIMAP);
if (!getFlag("tokenHint"))
{
setFlag("tokenHint", 1);
dsq->game->setControlHint(dsq->continuity.stringBank.get(4), false, false, false, 8);
}
}
// return the last one
return &gems.back();
}
void Continuity::entityDied(Entity *eDead)
{
if (statsAndAchievements)
{
statsAndAchievements->entityDied(eDead);
}
}
void Continuity::learnRecipe(Recipe *r, bool effects)
{
bool k = r->isKnown();
r->learn();
std::ostringstream os;
os << "learned recipe: " << r->result << " @ idx: " << r->index;
debugLog(os.str());
if (!k)
dsq->game->learnedRecipe(r, effects);
}
void Continuity::learnRecipe(const std::string &result, bool effects)
{
for (int i = 0; i < recipes.size(); i++)
{
if (nocasecmp(recipes[i].result, result)==0)
{
learnRecipe(&recipes[i], effects);
return;
}
}
}
void Continuity::reset()
{
dualFormMode = DUALFORM_LI;
dualFormCharge = 0;
if (dsq->game)
dsq->game->onContinuityReset();
speedMult = biteMult = fishPoison = defenseMult = 1;
speedMult2 = 1;
poison = 0;
energyMult = 0;
light = petPower = 0;
liPower = 0;
speedMultTimer.stop();
biteMultTimer.stop();
fishPoisonTimer.stop();
defenseMultTimer.stop();
invincibleTimer.stop();
regenTimer.stop();
tripTimer.stop();
energyTimer.stop();
poisonTimer.stop();
poisonBitTimer.stop();
webTimer.stop();
webBitTimer.stop();
lightTimer.stop();
petPowerTimer.stop();
loadTreasureData();
stringBank.load();
gems.clear();
beacons.clear();
worldMap.load();
naijaEats.clear();
foodSortType = 0;
ingredients.clear();
loadIngredientData(); // must be after clearing ingredients
loadPetData();
formUpgrades.clear();
auraType = AURA_NONE;
for (int i = 0; i < MAX_FLAGS; i++)
{
intFlags[i] = 0;
}
voiceOversPlayed.clear();
entityFlags.clear();
knowsSong.clear();
loadSongBank();
loadEatBank();
dsq->loadElementEffects();
form = FORM_NORMAL;
naijaModel = "Naija";
costume = "";
dsq->emote.load("data/naijaemote.txt");
worldType = WT_NORMAL;
zoom = Vector(1,1,0);
itemSlots.clear();
seconds = 0;
exp = 0;
hudVisible = true;
flags.clear();
stringFlags.clear();
story = 0;
maxHealth = 5;
health = maxHealth;
speedTypes.clear();
InStream inFile("data/speedtypes.txt");
int n;
float spd;
while (inFile >> n)
{
inFile >> spd;
speedTypes.push_back(spd);
}
if (!dsq->mod.isActive())
{
learnSong(SONG_SHIELDAURA);
}
initFoodSort();
core->resetTimer();
}
float Continuity::getSpeedType(int speedType)
{
if (speedType >= speedTypes.size() || speedType < 0)
{
std::ostringstream os;
os << "speedType: " << speedType << " out of range";
debugLog(os.str());
return 0;
}
return speedTypes[speedType];
}
void Continuity::achieve(const std::string &achievement)
{
}
void Continuity::flingMonkey(Entity *e)
{
statsAndAchievements->flingMonkey(e);
}