mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-12-01 15:35:47 +00:00
8472718fb7
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.
3551 lines
75 KiB
C++
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);
|
|
}
|