1 /*
2 Copyright (C) 2007, 2010 - Bit-Blot
3 
4 This file is part of Aquaria.
5 
6 Aquaria is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 #include "DSQ.h"
22 #include "Game.h"
23 #include "Avatar.h"
24 #include "ScriptedEntity.h"
25 #include "AutoMap.h"
26 #include "GridRender.h"
27 #include "DeflateCompressor.h"
28 
29 #include "tinyxml2.h"
30 using namespace tinyxml2;
31 
32 #define MAX_EATS			8
33 
34 const float webBitTime		= 2;
35 const float defenseTime		= 15;
36 const float speedTime		= 30;
37 const float	biteTime		= 30;
38 const float fishPoisonTime	= 30;
39 const float energyTime		= 45;
40 const float webTime			= 8;
41 const float petPowerTime	= 30;
42 const float lightTime		= 60;
43 
Profile()44 Profile::Profile()
45 {
46 	name = "save";
47 }
48 
Continuity()49 Continuity::Continuity()
50 {
51 	toggleMoveMode = false;
52 
53 	poisonBitTime = 1;
54 	poisonBitTimeAvatar = 2;
55 
56 	statsAndAchievements = 0;
57 }
58 
isIngredientFull(IngredientData * data)59 bool Continuity::isIngredientFull(IngredientData *data)
60 {
61 	for (int i = 0; i < ingredients.size(); i++)
62 	{
63 		if (nocasecmp(ingredients[i]->name, data->name)==0)
64 		{
65 			if (ingredients[i]->amount >= ingredients[i]->maxAmount)
66 				return true;
67 			else
68 				return false;
69 		}
70 	}
71 	return false;
72 }
73 
pickupIngredient(IngredientData * d,int amount,bool effects,bool learn)74 void Continuity::pickupIngredient(IngredientData *d, int amount, bool effects, bool learn)
75 {
76 	if(learn)
77 		learnRecipe(d->name, effects);
78 
79 	if (!getIngredientHeldByName(d->name))
80 	{
81 		ingredients.push_back(d);
82 	}
83 
84 	if (d->amount < d->maxAmount - amount)
85 	{
86 		d->amount += amount;
87 	}
88 	else
89 	{
90 		d->amount = d->maxAmount;
91 	}
92 }
93 
indexOfIngredientData(const IngredientData * data) const94 int Continuity::indexOfIngredientData(const IngredientData* data) const
95 {
96 	for (int i = 0; i < ingredientData.size(); i++)
97 	{
98 		if (ingredientData[i]->name == data->name)
99 		{
100 			return i;
101 		}
102 	}
103 	return -1;
104 }
105 
106 #define FOR_INGREDIENTDATA(x) for (int x = 0; x < ingredientData.size(); x++)
107 
getIngredientDataByName(const std::string & name)108 IngredientData *Continuity::getIngredientDataByName(const std::string &name)
109 {
110 	FOR_INGREDIENTDATA(i)
111 	{
112 		if (nocasecmp(ingredientData[i]->name, name)==0)
113 			return ingredientData[i];
114 	}
115 	return 0;
116 }
117 
getIngredientHeldByName(const std::string & name) const118 IngredientData *Continuity::getIngredientHeldByName(const std::string &name) const
119 {
120 	for (int i = 0; i < ingredients.size(); i++) {
121 		if (nocasecmp(ingredients[i]->name, name)==0)
122 			return ingredients[i];
123 	}
124 	return 0;
125 }
126 
getIngredientTypeFromName(const std::string & name) const127 IngredientType Continuity::getIngredientTypeFromName(const std::string &name) const
128 {
129 	if (name == "Meat")
130 		return IT_MEAT;
131 	else if (name == "Oil")
132 		return IT_OIL;
133 	else if (name == "Egg")
134 		return IT_EGG;
135 	else if (name == "Part")
136 		return IT_PART;
137 	else if (name == "Bone")
138 		return IT_BONE;
139 	else if (name == "Shell")
140 		return IT_SHELL;
141 	else if (name == "Tentacle")
142 		return IT_TENTACLE;
143 	else if (name == "Berry")
144 		return IT_BERRY;
145 	else if (name == "Leaf")
146 		return IT_LEAF;
147 	else if (name == "Poultice")
148 		return IT_POULTICE;
149 	else if (name == "IceChunk")
150 		return IT_ICECHUNK;
151 	else if (name == "Bulb")
152 		return IT_BULB;
153 	else if (name == "Roll")
154 		return IT_ROLL;
155 	else if (name == "Soup")
156 		return IT_SOUP;
157 	else if (name == "Cake")
158 		return IT_CAKE;
159 	else if (name == "IceCream")
160 		return IT_ICECREAM;
161 	else if (name == "Loaf")
162 		return IT_LOAF;
163 	else if (name == "PerogiType")
164 		return IT_PEROGI;
165 	else if (name == "Mushroom")
166 		return IT_MUSHROOM;
167 	else if (name == "Anything")
168 		return IT_ANYTHING;
169 	else if (name.length() && isdigit(name[0]))
170 		return (IngredientType)atoi(name.c_str());
171 
172 	return IT_NONE;
173 }
174 
getIngredientDisplayName(const std::string & name) const175 std::string Continuity::getIngredientDisplayName(const std::string& name) const
176 {
177 	IngredientNameMap::const_iterator it = ingredientDisplayNames.find(name);
178 	if (it != ingredientDisplayNames.end())
179 		return it->second;
180 
181 	return splitCamelCase(name);
182 }
183 
getIngredientHeldByIndex(int idx) const184 IngredientData *Continuity::getIngredientHeldByIndex(int idx) const
185 {
186 	if (idx < 0 || idx >= ingredients.size()) return 0;
187 	return ingredients[idx];
188 }
189 
getIngredientDataByIndex(int idx)190 IngredientData *Continuity::getIngredientDataByIndex(int idx)
191 {
192 	if (idx < 0 || idx >= ingredientData.size()) return 0;
193 	return ingredientData[idx];
194 }
195 
getIngredientDataSize() const196 int Continuity::getIngredientDataSize() const
197 {
198 	return (int)ingredientData.size();
199 }
200 
getIngredientHeldSize() const201 int Continuity::getIngredientHeldSize() const
202 {
203 	return (int)ingredients.size();
204 }
205 
Recipe()206 Recipe::Recipe()
207 {
208 	known = false;
209 	index = -1;
210 }
211 
clear()212 void Recipe::clear()
213 {
214 	types.clear();
215 	names.clear();
216 	result = "";
217 	resultDisplayName = "";
218 	known = false;
219 }
220 
learn()221 void Recipe::learn()
222 {
223 	known = true;
224 }
225 
addName(const std::string & name)226 void Recipe::addName(const std::string &name)
227 {
228 	int i = 0;
229 	for (; i < names.size(); i++)
230 	{
231 		if (names[i].name == name)
232 		{
233 			names[i].amount++;
234 			break;
235 		}
236 	}
237 	if (i == names.size())
238 		names.push_back(RecipeName(name));
239 }
240 
addType(IngredientType type,const std::string & typeName)241 void Recipe::addType(IngredientType type, const std::string &typeName)
242 {
243 	int i = 0;
244 	for (; i < types.size(); i++)
245 	{
246 		if (types[i].type == type)
247 		{
248 			types[i].amount++;
249 			break;
250 		}
251 	}
252 	if (i == types.size())
253 		types.push_back(RecipeType(type, typeName));
254 }
255 
initFoodSort()256 void Continuity::initFoodSort()
257 {
258 	// move to init
259 	sortByType.clear();
260 
261 	sortByType.push_back(FoodSortOrder(IT_POULTICE));
262 	sortByType.push_back(FoodSortOrder(IT_ROLL));
263 	sortByType.push_back(FoodSortOrder(IT_CAKE));
264 	sortByType.push_back(FoodSortOrder(IT_SOUP));
265 	sortByType.push_back(FoodSortOrder(IT_LOAF));
266 	sortByType.push_back(FoodSortOrder(IT_PEROGI));
267 	sortByType.push_back(FoodSortOrder(IT_LEAF));
268 	sortByType.push_back(FoodSortOrder(IT_MEAT));
269 	sortByType.push_back(FoodSortOrder(IT_OIL));
270 	sortByType.push_back(FoodSortOrder(IT_ICECREAM));
271 	sortByType.push_back(FoodSortOrder(IT_BERRY));
272 	sortByType.push_back(FoodSortOrder(IT_MUSHROOM));
273 	sortByType.push_back(FoodSortOrder(IT_BULB));
274 	sortByType.push_back(FoodSortOrder(IT_EGG));
275 	sortByType.push_back(FoodSortOrder(IT_SHELL));
276 	sortByType.push_back(FoodSortOrder(IT_PART));
277 	sortByType.push_back(FoodSortOrder(IT_TENTACLE));
278 	sortByType.push_back(FoodSortOrder(IT_ICECHUNK));
279 	sortByType.push_back(FoodSortOrder(IT_BONE));
280 	sortByType.push_back(FoodSortOrder(IT_FOOD));
281 
282 	sortByHeal.clear();
283 	sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_MAXHP));
284 	for (int i = 10; i >= -10; i--)
285 	{
286 		if (i != 0)
287 			sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_HP, "", i));
288 	}
289 	sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_DEFENSE));
290 	sortByHeal.push_back(FoodSortOrder(IT_NONE, IET_SPEED));
291 
292 	sortByIngredients.clear();
293 	for (int i = 0; i < IT_INGREDIENTSEND; i++)
294 	{
295 		sortByIngredients.push_back(FoodSortOrder((IngredientType)i));
296 	}
297 }
298 
sortFood()299 void Continuity::sortFood()
300 {
301 	std::vector<FoodSortOrder> sortOrder;
302 
303 	bool doSort = true;
304 
305 	switch (dsq->continuity.foodSortType)
306 	{
307 	/*
308 	case FOODSORT_UNSORTED:
309 		sortOrder = sortByUnsort;
310 	break;
311 	*/
312 	case FOODSORT_BYTYPE:
313 		sortOrder = sortByType;
314 	break;
315 	case FOODSORT_BYHEAL:
316 		sortOrder = sortByHeal;
317 	break;
318 	case FOODSORT_BYINGREDIENT:
319 		sortOrder = sortByIngredients;
320 	break;
321 
322 	}
323 
324 	//IngredientData *plantLeaf = dsq->continuity.getIngredientHeldByName("PlantLeaf");
325 	//int oldHeld = plantLeaf->held;
326 
327 	if (doSort)
328 	{
329 
330 
331 		std::vector<IngredientData*> sort;
332 
333 		for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
334 		{
335 			dsq->continuity.ingredients[i]->sorted = false;
336 		}
337 
338 		for (int j = 0; j < sortOrder.size(); j++)
339 		{
340 			for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
341 			{
342 				IngredientData *data = dsq->continuity.ingredients[i];
343 				if (!data->sorted)
344 				{
345 					if (sortOrder[j].type == IT_NONE || sortOrder[j].type == data->type)
346 					{
347 						if (!sortOrder[j].name.empty())
348 						{
349 							if (sortOrder[j].name == data->name)
350 							{
351 								data->sorted = true;
352 								sort.push_back(data);
353 							}
354 						}
355 						else if (sortOrder[j].effectType != IET_NONE)
356 						{
357 							for (int c = 0; c < data->effects.size(); c++)
358 							{
359 								if (data->effects[c].type == sortOrder[j].effectType)
360 								{
361 									if (sortOrder[j].effectAmount == 0 || data->effects[c].magnitude == sortOrder[j].effectAmount)
362 									{
363 										data->sorted = true;
364 										sort.push_back(data);
365 									}
366 								}
367 							}
368 						}
369 						else
370 						{
371 							data->sorted = true;
372 							sort.push_back(data);
373 						}
374 					}
375 				}
376 			}
377 		}
378 
379 		for (int i = 0; i < dsq->continuity.ingredients.size(); i++)
380 		{
381 			IngredientData *data = dsq->continuity.ingredients[i];
382 			if (!data->sorted)
383 			{
384 				data->sorted = true;
385 				sort.push_back(data);
386 			}
387 		}
388 
389 		ingredients.clear();
390 		for (int i = 0; i < sort.size(); i++) {
391 			ingredients.push_back(sort[i]);
392 		}
393 		sort.clear();
394 		//dsq->continuity.ingredients = sort;
395 	}
396 
397 	//IngredientData *plantLeaf2 = dsq->continuity.getIngredientHeldByName("PlantLeaf");
398 	//int newHeld = plantLeaf2->held;
399 }
400 
setRegen(float t)401 void Continuity::setRegen(float t)
402 {
403 	regenTimer.start(t);
404 }
405 
setTrip(float t)406 void Continuity::setTrip(float t)
407 {
408 	tripTimer.start(t);
409 	dsq->game->avatar->applyTripEffects();
410 }
411 
setInvincible(float t)412 void Continuity::setInvincible(float t)
413 {
414 	invincibleTimer.start(t);
415 }
416 
setSpeedMultiplier(float s,float t)417 void Continuity::setSpeedMultiplier(float s, float t)
418 {
419 	speedMultTimer.start(t);
420 	speedMult = s;
421 }
422 
setEnergy(float m,float t)423 void Continuity::setEnergy(float m, float t)
424 {
425 	energyTimer.start(t);
426 	energyMult = m;
427 }
428 
setLiPower(float m,float t)429 void Continuity::setLiPower(float m, float t)
430 {
431 	liPowerTimer.start(t);
432 	liPower = m;
433 }
434 
setPetPower(float m,float t)435 void Continuity::setPetPower(float m, float t)
436 {
437 	petPower = m;
438 	petPowerTimer.start(t);
439 }
440 
setLight(float m,float t)441 void Continuity::setLight(float m, float t)
442 {
443 	light = m;
444 	lightTimer.start(t);
445 }
446 
setWeb(float t)447 void Continuity::setWeb(float t)
448 {
449 	webTimer.start(t);
450 
451 	webBitTimer.start(webBitTime);
452 
453 	if (dsq->game->avatar)
454 	{
455 		if (!dsq->game->avatar->web)
456 		{
457 			dsq->game->avatar->createWeb();
458 		}
459 	}
460 }
461 
setPoison(float m,float t)462 void Continuity::setPoison(float m, float t)
463 {
464 	poisonTimer.start(t);
465 	poison = m;
466 
467 	if (poison)
468 	{
469 		poisonBitTimer.start(poisonBitTime);
470 	}
471 }
472 
cureAllStatus()473 void Continuity::cureAllStatus()
474 {
475 	setPoison(0,0);
476 
477 	if (dsq->game->avatar)
478 	{
479 		dsq->game->avatar->setBlind(0);
480 	}
481 }
482 
setDefenseMultiplier(float s,float t)483 void Continuity::setDefenseMultiplier(float s, float t)
484 {
485 	defenseMultTimer.start(t);
486 	defenseMult = s;
487 }
488 
setBiteMultiplier(float m,float t)489 void Continuity::setBiteMultiplier(float m, float t)
490 {
491 	biteMultTimer.start(t);
492 	biteMult = m;
493 }
494 
setFishPoison(float m,float t)495 void Continuity::setFishPoison(float m, float t)
496 {
497 	fishPoisonTimer.start(t);
498 	fishPoison = m;
499 }
500 
getIEString(IngredientData * data,int i)501 std::string Continuity::getIEString(IngredientData *data, int i)
502 {
503 	if (i < 0 || i >= data->effects.size()) return "";
504 
505 	IngredientEffect fx = data->effects[i];
506 	IngredientEffectType useType = fx.type;
507 
508 	std::ostringstream os;
509 
510 	switch(useType)
511 	{
512 	case IET_HP:
513 		if (fx.magnitude > 0)
514 		{
515 			std::ostringstream os;
516 			os << dsq->continuity.stringBank.get(200) << " ";
517 			os << dsq->continuity.stringBank.get(100) << " ";
518 			os << fx.magnitude;
519 			return os.str();
520 		}
521 		else
522 		{
523 			std::ostringstream os;
524 			os << dsq->continuity.stringBank.get(200) << " ";
525 			os << dsq->continuity.stringBank.get(101) << " ";
526 			os << fabsf(fx.magnitude);
527 			return os.str();
528 		}
529 	break;
530 	case IET_MAXHP:
531 		return dsq->continuity.stringBank.get(201);
532 	break;
533 	case IET_DEFENSE:
534 		os << dsq->continuity.stringBank.get(202);
535 		os << " " << fx.magnitude << " " << dsq->continuity.stringBank.get(205) << " " << defenseTime << " " << dsq->continuity.stringBank.get(203);
536 		return os.str();
537 	break;
538 	case IET_SPEED:
539 		os << dsq->continuity.stringBank.get(204) << " " << fx.magnitude;
540 		os << " " << dsq->continuity.stringBank.get(205) << " " << speedTime << " " << dsq->continuity.stringBank.get(203);
541 		return os.str();
542 	break;
543 	case IET_REGEN:
544 		os << dsq->continuity.stringBank.get(206) << " " << fx.magnitude;
545 		return os.str();
546 	break;
547 	case IET_TRIP:
548 		return dsq->continuity.stringBank.get(207);
549 	break;
550 	case IET_EAT:
551 		return dsq->continuity.stringBank.get(208);
552 	break;
553 	case IET_BITE:
554 		os << dsq->continuity.stringBank.get(209);
555 		os << " " << dsq->continuity.stringBank.get(205) << " " << biteTime << " " << dsq->continuity.stringBank.get(203);
556 		return os.str();
557 	break;
558 	case IET_FISHPOISON:
559 		os << dsq->continuity.stringBank.get(217);
560 		os << " " << dsq->continuity.stringBank.get(205) << " " << fishPoisonTime << " " << dsq->continuity.stringBank.get(203);
561 		return os.str();
562 	break;
563 	case IET_INVINCIBLE:
564 		os << dsq->continuity.stringBank.get(210);
565 		os << " " << dsq->continuity.stringBank.get(205) << " " << (fx.magnitude*5) << " " << dsq->continuity.stringBank.get(203);
566 		return os.str();
567 		//return dsq->continuity.stringBank.get(210);
568 	break;
569 	case IET_ENERGY:
570 		os << dsq->continuity.stringBank.get(211) << " " << fx.magnitude;
571 		os << " " << dsq->continuity.stringBank.get(205) << " " << energyTime << " " << dsq->continuity.stringBank.get(203);
572 		return os.str();
573 	break;
574 	case IET_BLIND:
575 		return dsq->continuity.stringBank.get(212);
576 	break;
577 	case IET_POISON:
578 		if (fx.magnitude < 0)
579 			return dsq->continuity.stringBank.get(213);
580 		else
581 			return dsq->continuity.stringBank.get(214);
582 	break;
583 	case IET_YUM:
584 		return dsq->continuity.stringBank.get(215);
585 	break;
586 	case IET_WEB:
587 		os << dsq->continuity.stringBank.get(219);
588 		os << " " << dsq->continuity.stringBank.get(205) << " " << webTime << " " << dsq->continuity.stringBank.get(203);
589 		return os.str();
590 	break;
591 	case IET_ALLSTATUS:
592 		return dsq->continuity.stringBank.get(218);
593 	break;
594 	case IET_PETPOWER:
595 		os << dsq->continuity.stringBank.get(216);
596 		os << " " << dsq->continuity.stringBank.get(205) << " " << petPowerTime << " " << dsq->continuity.stringBank.get(203);
597 		return os.str();
598 	break;
599 	case IET_LIGHT:
600 		os << dsq->continuity.stringBank.get(220);
601 		os << " " << dsq->continuity.stringBank.get(205) << " " << lightTime << " " << dsq->continuity.stringBank.get(203);
602 		return os.str();
603 	break;
604 	case IET_LI:
605 		return dsq->continuity.stringBank.get(227);
606 	break;
607 	case IET_SCRIPT:
608 		if(dsq->game->cookingScript)
609 		{
610 			std::string ret = "";
611 			dsq->game->cookingScript->call("getIngredientEffectString", data->name.c_str(), &ret);
612 			return ret;
613 		}
614 	break;
615 	}
616 
617 	return "";
618 }
619 
getAllIEString(IngredientData * data)620 std::string Continuity::getAllIEString(IngredientData *data)
621 {
622 	std::ostringstream os;
623 
624 	for (int i = 0; i < data->effects.size(); i++)
625 	{
626 		os << getIEString(data, i) << "\n";
627 	}
628 
629 	return os.str();
630 }
631 
632 // returns true if eaten
applyIngredientEffects(IngredientData * data)633 bool Continuity::applyIngredientEffects(IngredientData *data)
634 {
635 	bool eaten = true;
636 	float y =0;
637 	for (int i = 0; i < data->effects.size(); i++)
638 	{
639 		y = 300 + i * 40;
640 		IngredientEffect fx = data->effects[i];
641 		IngredientEffectType useType = fx.type;
642 		if (fx.type == IET_RANDOM)
643 		{
644 		}
645 		switch(useType)
646 		{
647 		case IET_HP:
648 		{
649 			dsq->game->avatar->heal(fx.magnitude);
650 			debugLog("ingredient effect: hp");
651 			if (fx.magnitude > 0)
652 			{
653 				dsq->centerMessage(getIEString(data, i), y);
654 
655 				core->sound->playSfx("CollectMana");
656 
657 				dsq->overlay2->color = Vector(0.5, 0.5, 1);
658 				dsq->overlay2->alpha.ensureData();
659 				dsq->overlay2->alpha.data->path.clear();
660 				dsq->overlay2->alpha.data->path.addPathNode(0, 0);
661 				dsq->overlay2->alpha.data->path.addPathNode(0.5, 0.5);
662 				dsq->overlay2->alpha.data->path.addPathNode(0, 1);
663 				dsq->overlay2->alpha.startPath(1);
664 			}
665 			else
666 			{
667 				dsq->centerMessage(getIEString(data, i), y, 1);
668 
669 				dsq->game->avatar->playHitSound();
670 			}
671 		}
672 		break;
673 		case IET_MAXHP:
674 		{
675 			dsq->game->avatar->heal(dsq->game->avatar->maxHealth);
676 			debugLog("ingredient effect: maxhp");
677 			core->sound->playSfx("CollectMana");
678 
679 			dsq->overlay2->color = Vector(0.5, 0.5, 1);
680 			dsq->overlay2->alpha.ensureData();
681 			dsq->overlay2->alpha.data->path.clear();
682 			dsq->overlay2->alpha.data->path.addPathNode(0, 0);
683 			dsq->overlay2->alpha.data->path.addPathNode(0.5, 0.5);
684 			dsq->overlay2->alpha.data->path.addPathNode(0, 1);
685 			dsq->overlay2->alpha.startPath(2);
686 
687 			dsq->centerMessage(getIEString(data, i), y);
688 		}
689 		break;
690 		case IET_DEFENSE:
691 		{
692 
693 			debugLog("ingredient effect: defense");
694 
695 			if (fx.magnitude <= 1)
696 				dsq->continuity.setDefenseMultiplier(0.75, defenseTime);
697 			else if (fx.magnitude == 2)
698 				dsq->continuity.setDefenseMultiplier(0.5, defenseTime);
699 			else if (fx.magnitude == 3)
700 				dsq->continuity.setDefenseMultiplier(0.3, defenseTime);
701 			else
702 				debugLog("unsupported magnitude for defense");
703 
704 			dsq->centerMessage(getIEString(data, i), y);
705 
706 			dsq->sound->playSfx("defense");
707 		}
708 		break;
709 		case IET_SPEED:
710 		{
711 
712 			dsq->continuity.setSpeedMultiplier(1.0f + fx.magnitude*0.5f, speedTime);
713 			debugLog("ingredient effect: speed");
714 
715 			dsq->centerMessage(getIEString(data, i), y);
716 
717 			dsq->sound->playSfx("speedup");
718 		}
719 		break;
720 		case IET_REGEN:
721 		{
722 			dsq->continuity.setRegen(fx.magnitude*5);
723 			debugLog("ingredient effect: regen");
724 
725 			dsq->centerMessage(getIEString(data, i), y);
726 
727 			dsq->sound->playSfx("regen");
728 		}
729 		break;
730 		case IET_TRIP:
731 		{
732 			dsq->continuity.setTrip(fx.magnitude*30);
733 			debugLog("ingredient effect: trip");
734 
735 			dsq->centerMessage(getIEString(data, i), y);
736 		}
737 		break;
738 		case IET_EAT:
739 		{
740 			EatData *getter = dsq->continuity.getEatData(fx.string);
741 			if (getter)
742 			{
743 				EatData setter = *getter;
744 				dsq->continuity.eatBeast(setter);
745 				debugLog("ate: " + setter.name);
746 			}
747 			debugLog("ingredient effect: eat");
748 
749 			dsq->centerMessage(getIEString(data, i), y);
750 
751 			dsq->sound->playSfx("gulp");
752 		}
753 		break;
754 		case IET_BITE:
755 		{
756 			dsq->continuity.setBiteMultiplier(1.0f + fx.magnitude, biteTime);
757 			debugLog("ingredient effect: bite");
758 
759 			dsq->centerMessage(getIEString(data, i), y);
760 
761 			dsq->sound->playSfx("bite");
762 		}
763 		break;
764 		case IET_FISHPOISON:
765 		{
766 			dsq->continuity.setFishPoison(1.0f * fx.magnitude, fishPoisonTime);
767 			debugLog("ingredient effect: fishPoison");
768 
769 			dsq->centerMessage(getIEString(data, i), y);
770 
771 			dsq->sound->playSfx("poison");
772 		}
773 		break;
774 		case IET_INVINCIBLE:
775 		{
776 			dsq->continuity.setInvincible(fx.magnitude*5);
777 
778 			dsq->centerMessage(getIEString(data, i), y);
779 
780 			dsq->sound->playSfx("invincible");
781 		}
782 		break;
783 		case IET_ENERGY:
784 		{
785 			dsq->continuity.setEnergy(fx.magnitude, energyTime);
786 
787 			dsq->centerMessage(getIEString(data, i), y);
788 
789 			dsq->sound->playSfx("energy");
790 		}
791 		break;
792 		case IET_BLIND:
793 		{
794 			if (fx.magnitude < 0)
795 			{
796 				dsq->game->avatar->setBlind(0);
797 				dsq->centerMessage(getIEString(data, i), y);
798 				dsq->sound->playSfx("regen");
799 			}
800 		}
801 		break;
802 		case IET_POISON:
803 		{
804 			if (fx.magnitude < 0)
805 			{
806 				dsq->continuity.setPoison(0,0);
807 				dsq->centerMessage(getIEString(data, i), y);
808 				dsq->sound->playSfx("regen");
809 			}
810 			else
811 			{
812 				dsq->sound->playSfx("poison");
813 				float t = 30;
814 				dsq->continuity.setPoison(fx.magnitude,t);
815 				dsq->centerMessage(getIEString(data, i), y, 1);
816 			}
817 		}
818 		break;
819 		case IET_YUM:
820 		{
821 			dsq->centerMessage(getIEString(data, i), y);
822 			dsq->sound->playSfx("naijayum");
823 		}
824 		break;
825 		case IET_WEB:
826 		{
827 			dsq->sound->playSfx("spiderweb");
828 
829 			dsq->centerMessage(getIEString(data, i), y);
830 
831 			dsq->continuity.setWeb(webTime);
832 			//dsq->centerMessage(dsq->continuity.stringBank.get(216), y);
833 		}
834 		break;
835 		case IET_ALLSTATUS:
836 		{
837 			dsq->sound->playSfx("regen");
838 
839 			dsq->continuity.cureAllStatus();
840 			dsq->centerMessage(getIEString(data, i), y);
841 		}
842 		break;
843 		case IET_PETPOWER:
844 		{
845 			dsq->sound->playSfx("nautilus");
846 
847 			dsq->continuity.setPetPower(fx.magnitude, petPowerTime);
848 
849 			dsq->centerMessage(getIEString(data, i), y);
850 		}
851 		break;
852 		case IET_LIGHT:
853 		{
854 			dsq->sound->playSfx("sunform");
855 
856 			dsq->continuity.setLight(fx.magnitude, lightTime);
857 
858 			dsq->centerMessage(getIEString(data, i), y);
859 		}
860 		break;
861 		case IET_LI:
862 		{
863 			// this should do nothing, its just here to catch the ingredient effect so it doesn't
864 			// give the "default:" error message
865 			// this item should only affect li if naija drops it and li eats it.
866 		}
867 		break;
868 		case IET_SCRIPT:
869 		{
870 			// If this fails, it will still be eaten
871 			if(dsq->game->cookingScript)
872 				dsq->game->cookingScript->call("useIngredient", data->name.c_str(), &eaten);
873 		}
874 		break;
875 		default:
876 		{
877 			char str[256];
878 			sprintf((char*)&str, "ingredient effect not defined, index[%d]", int(useType));
879 			errorLog(str);
880 			eaten = false;
881 		}
882 		break;
883 		}
884 	}
885 	return eaten;
886 }
887 
getIngredientAffectsString(IngredientData * data)888 std::string Continuity::getIngredientAffectsString(IngredientData *data)
889 {
890 	return getAllIEString(data);
891 }
892 
loadTreasureData()893 void Continuity::loadTreasureData()
894 {
895 	treasureData.clear();
896 
897 	std::string line, gfx, file;
898 	int num, use;
899 	float sz;
900 	bool found = false;
901 	if (dsq->mod.isActive())
902 	{
903 		file = dsq->mod.getPath() + "treasures.txt";
904 		if(exists(file))
905 			found = true;
906 	}
907 
908 	if(!found)
909 		file = "data/treasures.txt";
910 
911 	InStream in2(file.c_str());
912 	while (std::getline(in2, line))
913 	{
914 		std::istringstream is(line);
915 		is >> num >> gfx >> sz >> use;
916 		if (sz == 0)
917 			sz = 1;
918 		TreasureDataEntry d;
919 		d.gfx = gfx;
920 		d.sz = sz;
921 		d.use = use;
922 		treasureData[num] = d;
923 	}
924 	in2.close();
925 }
926 
clearIngredientData()927 void Continuity::clearIngredientData()
928 {
929 	for (IngredientDatas::iterator i = ingredientData.begin(); i != ingredientData.end(); ++ i)
930 	{
931 		delete *i;
932 	}
933 	ingredientData.clear();
934 }
935 
loadIngredientData()936 void Continuity::loadIngredientData()
937 {
938 	if(ingredients.size())
939 	{
940 		debugLog("Can't reload ingredient data, inventory is not empty");
941 		return; // ... because otherwise there would be dangling pointers and it would crash.
942 	}
943 
944 	clearIngredientData();
945 	ingredientDescriptions.clear();
946 	ingredientDisplayNames.clear();
947 	recipes.clear();
948 
949 	loadIngredientDisplayNames("data/ingredientnames.txt");
950 
951 	std::string fname = localisePath("data/ingredientnames.txt");
952 	loadIngredientDisplayNames(fname);
953 
954 	if(dsq->mod.isActive())
955 	{
956 		fname = localisePath(dsq->mod.getPath() + "ingredientnames.txt", dsq->mod.getPath());
957 		loadIngredientDisplayNames(fname);
958 	}
959 
960 	if(dsq->mod.isActive())
961 	{
962 		//load mod ingredients
963 		loadIngredientData(dsq->mod.getPath() + "ingredients.txt");
964 	}
965 
966 	//load ingredients for the main game
967 	if(ingredientData.empty() && recipes.empty())
968 	{
969 		loadIngredientData("data/ingredients.txt");
970 	}
971 }
972 
loadIngredientData(const std::string & file)973 void Continuity::loadIngredientData(const std::string &file)
974 {
975 	std::string line, name, gfx, type, effects;
976 
977 	clearIngredientData();
978 	recipes.clear();
979 
980 	InStream in(file.c_str());
981 
982 	bool recipes = false;
983 	bool extradata = false;
984 	while (std::getline(in, line))
985 	{
986 		std::istringstream inLine(line);
987 
988 		inLine >> name;
989 
990 		if (name == "==Recipes==")
991 		{
992 			recipes = true;
993 			break;
994 		}
995 		else if(name == "==Extra==")
996 		{
997 			extradata = true;
998 			break;
999 		}
1000 		inLine >> gfx >> type;
1001 
1002 		std::getline(inLine, effects);
1003 
1004 		IngredientData *data = new IngredientData(name, gfx, getIngredientTypeFromName(type));
1005 
1006 		if (!effects.empty())
1007 		{
1008 			int p1 = effects.find("(");
1009 			int p2 = effects.find(")");
1010 			if (p1 != std::string::npos && p2 != std::string::npos)
1011 			{
1012 				effects = effects.substr(p1+1, p2-(p1+1));
1013 				std::istringstream fxLine(effects);
1014 				std::string bit;
1015 				while (fxLine >> bit)
1016 				{
1017 					IngredientEffect fx;
1018 
1019 					if (bit.find("eat:") != std::string::npos)
1020 					{
1021 						int pos = bit.find(':')+1;
1022 						fx.string = bit.substr(pos, bit.size()-pos);
1023 						fx.type = IET_EAT;
1024 					}
1025 					else if (bit.find("yum") != std::string::npos)
1026 					{
1027 						fx.type = IET_YUM;
1028 					}
1029 					else if (bit.find("petpower") != std::string::npos)
1030 					{
1031 						fx.type = IET_PETPOWER;
1032 					}
1033 					else if (bit.find("web") != std::string::npos)
1034 					{
1035 						fx.type = IET_WEB;
1036 					}
1037 					else if (bit.find("energy") != std::string::npos)
1038 					{
1039 						fx.type = IET_ENERGY;
1040 					}
1041 					else if (bit.find("poison") != std::string::npos)
1042 					{
1043 						fx.type = IET_POISON;
1044 					}
1045 					else if (bit.find("blind") != std::string::npos)
1046 					{
1047 						fx.type = IET_BLIND;
1048 					}
1049 					else if (bit.find("allstatus") != std::string::npos)
1050 					{
1051 						fx.type = IET_ALLSTATUS;
1052 					}
1053 					else if (bit.find("maxhp") != std::string::npos)
1054 					{
1055 						fx.type = IET_MAXHP;
1056 					}
1057 					else if (bit.find("invincible") != std::string::npos)
1058 					{
1059 						fx.type = IET_INVINCIBLE;
1060 					}
1061 					else if (bit.find("trip") != std::string::npos)
1062 					{
1063 						fx.type = IET_TRIP;
1064 					}
1065 					else if (bit.find("defense") != std::string::npos)
1066 					{
1067 						fx.type = IET_DEFENSE;
1068 					}
1069 					else if (bit.find("speed") != std::string::npos)
1070 					{
1071 						fx.type = IET_SPEED;
1072 					}
1073 					else if (bit.find("random") != std::string::npos)
1074 					{
1075  						fx.type = IET_RANDOM;
1076 					}
1077 					else if (bit.find("bite") != std::string::npos)
1078 					{
1079 						fx.type = IET_BITE;
1080 					}
1081 					else if (bit.find("fishPoison") != std::string::npos)
1082 					{
1083 						fx.type = IET_FISHPOISON;
1084 					}
1085 					else if (bit.find("regen") != std::string::npos)
1086 					{
1087 						fx.type = IET_REGEN;
1088 					}
1089 					else if (bit.find("light") != std::string::npos)
1090 					{
1091 						fx.type = IET_LIGHT;
1092 					}
1093 					else if (bit.find("hp") != std::string::npos)
1094 					{
1095 						fx.type = IET_HP;
1096 					}
1097 					else if (bit.find("li") != std::string::npos)
1098 					{
1099 						fx.type = IET_LI;
1100 					}
1101 					else if (bit.find("script") != std::string::npos)
1102 					{
1103 						fx.type = IET_SCRIPT;
1104 					}
1105 
1106 					int c = 0;
1107 					while (c < bit.size())
1108 					{
1109 						if (bit[c] == '+')
1110 							fx.magnitude += 1;
1111 						else if (bit[c] == '-')
1112 							fx.magnitude -= 1;
1113 						else if (bit[c] == '~')
1114 							fx.magnitude += 0.1f;
1115 						c++;
1116 					}
1117 					data->effects.push_back(fx);
1118 				}
1119 			}
1120 		}
1121 
1122 		ingredientData.push_back(data);
1123 	}
1124 
1125 	if(extradata)
1126 	{
1127 		while (std::getline(in, line))
1128 		{
1129 			SimpleIStringStream inLine(line.c_str(), SimpleIStringStream::REUSE);
1130 			int maxAmount = MAX_INGREDIENT_AMOUNT;
1131 			int rotKind = 1;
1132 			inLine >> name >> maxAmount >> rotKind;
1133 			if (name == "==Recipes==")
1134 			{
1135 				recipes = true;
1136 				break;
1137 			}
1138 			IngredientData *data = getIngredientDataByName(name);
1139 			if(!data)
1140 			{
1141 				errorLog("Specifying data for undefined ingredient: " + name);
1142 				continue;
1143 			}
1144 
1145 			data->maxAmount = maxAmount;
1146 			data->rotKind = rotKind;
1147 		}
1148 	}
1149 
1150 	if (recipes)
1151 	{
1152 		bool quitNext = false;
1153 
1154 		int index=0;
1155 		Recipe r;
1156 		while (in >> name)
1157 		{
1158 			if (name == "+")
1159 			{
1160 				continue;
1161 			}
1162 			else if (name == "=")
1163 			{
1164 				quitNext = true;
1165 				continue;
1166 			}
1167 			else
1168 			{
1169 				if (quitNext)
1170 				{
1171 					r.result = name;
1172 					r.resultDisplayName = getIngredientDisplayName(name);
1173 				}
1174 				else
1175 				{
1176 					IngredientType it = getIngredientTypeFromName(name);
1177 					if (it == IT_NONE)
1178 					{
1179 						r.addName(name);
1180 					}
1181 					else
1182 					{
1183 						r.addType(it, name);
1184 					}
1185 				}
1186 			}
1187 
1188 			if (quitNext)
1189 			{
1190 				r.index = index;
1191 				this->recipes.push_back(r);
1192 				r.clear();
1193 				quitNext = false;
1194 				index++;
1195 			}
1196 		}
1197 	}
1198 	in.close();
1199 }
1200 
loadIngredientDisplayNames(const std::string & file)1201 void Continuity::loadIngredientDisplayNames(const std::string& file)
1202 {
1203 	InStream in(file);
1204 	if (!in)
1205 		return;
1206 
1207 	std::string line, name, text;
1208 	while (std::getline(in, line))
1209 	{
1210 		size_t pos = line.find(' ');
1211 		if (pos == std::string::npos)
1212 			continue;
1213 		name = line.substr(0, pos);
1214 		text = line.substr(pos + 1);
1215 		ingredientDisplayNames[name] = text;
1216 	}
1217 }
1218 
learnFormUpgrade(FormUpgradeType form)1219 void Continuity::learnFormUpgrade(FormUpgradeType form)
1220 {
1221 	formUpgrades[form] = true;
1222 }
1223 
hasFormUpgrade(FormUpgradeType form)1224 bool Continuity::hasFormUpgrade(FormUpgradeType form)
1225 {
1226 	return formUpgrades[form];
1227 }
1228 
getInternalFormName()1229 std::string Continuity::getInternalFormName()
1230 {
1231 	switch(form)
1232 	{
1233 	case FORM_NORMAL:
1234 		return "normal";
1235 	case FORM_ENERGY:
1236 		return "energy";
1237 	case FORM_NATURE:
1238 		return "nature";
1239 	case FORM_BEAST:
1240 		return "beast";
1241 	case FORM_FISH:
1242 		return "fish";
1243 	case FORM_SPIRIT:
1244 		return "spirit";
1245 	case FORM_SUN:
1246 		return "sun";
1247 	case FORM_DUAL:
1248 		return "dual";
1249 	}
1250 	return "";
1251 }
1252 
loadIntoSongBank(const std::string & file)1253 void Continuity::loadIntoSongBank(const std::string &file)
1254 {
1255 	if(!exists(file))
1256 		return;
1257 
1258 	XMLDocument doc;
1259 	XMLError err = readXML(file, doc);
1260 	if(err == XML_ERROR_EMPTY_DOCUMENT)
1261 		return;
1262 	if(err != XML_SUCCESS)
1263 	{
1264 		errorLog("Failed to load song bank: Malformed XML");
1265 		return;
1266 	}
1267 
1268 	XMLElement *song = doc.FirstChildElement("Song");
1269 	while (song)
1270 	{
1271 		Song s;
1272 
1273 		if (song->Attribute("notes"))
1274 		{
1275 			std::string strng = song->Attribute("notes");
1276 			std::istringstream is(strng);
1277 			int note = 0;
1278 			while (is >> note)
1279 			{
1280 				s.notes.push_back(note);
1281 			}
1282 		}
1283 
1284 		if (song->Attribute("script"))
1285 		{
1286 			s.script = atoi(song->Attribute("script"));
1287 		}
1288 
1289 		int slot = -1;
1290 		if (song->Attribute("slot"))
1291 		{
1292 			slot = atoi(song->Attribute("slot"));
1293 			if (slot != -1)
1294 			{
1295 				if (song->Attribute("description"))
1296 				{
1297 					songSlotDescriptions[slot] = song->Attribute("description");
1298 				}
1299 				if (song->Attribute("vox"))
1300 				{
1301 					songSlotVox[slot] = song->Attribute("vox");
1302 				}
1303 			}
1304 		}
1305 		int idx = atoi(song->Attribute("idx"));
1306 		songBank[idx] = s;
1307 		if (slot != -1)
1308 		{
1309 			songSlotsToType[slot] = idx;
1310 			songTypesToSlot[idx] = slot;
1311 		}
1312 		if (song->Attribute("name"))
1313 		{
1314 			songSlotNames[slot] = song->Attribute("name");
1315 		}
1316 		song = song->NextSiblingElement("Song");
1317 	}
1318 }
1319 
loadSongBank()1320 void Continuity::loadSongBank()
1321 {
1322 	songSlotDescriptions.clear();
1323 	songSlotVox.clear();
1324 	songSlotsToType.clear();
1325 	songTypesToSlot.clear();
1326 	songSlotNames.clear();
1327 	songBank.clear();
1328 
1329 	loadIntoSongBank(localisePath("data/songs.xml"));
1330 
1331 	if (dsq->mod.isActive())
1332 	{
1333 		loadIntoSongBank(localisePath(dsq->mod.getPath() + "scripts/songs.xml", dsq->mod.getPath()));
1334 	}
1335 }
1336 
getSongTypeBySlot(int slot)1337 int Continuity::getSongTypeBySlot(int slot)
1338 {
1339 	return songSlotsToType[slot];
1340 }
1341 
getSongSlotByType(int type)1342 int Continuity::getSongSlotByType(int type)
1343 {
1344 	return songTypesToSlot[type];
1345 }
1346 
getDescriptionForSongSlot(int songSlot)1347 std::string Continuity::getDescriptionForSongSlot(int songSlot)
1348 {
1349 	return songSlotDescriptions[songSlot];
1350 }
1351 
getVoxForSongSlot(int songSlot)1352 std::string Continuity::getVoxForSongSlot(int songSlot)
1353 {
1354 	return songSlotVox[songSlot];
1355 }
1356 
getEatData(const std::string & name)1357 EatData *Continuity::getEatData(const std::string &name)
1358 {
1359 	for (int i = 0; i < eats.size(); i++)
1360 	{
1361 		if (eats[i].name == name)
1362 			return &eats[i];
1363 	}
1364 	return 0;
1365 }
1366 
loadEatBank()1367 void Continuity::loadEatBank()
1368 {
1369 	eats.clear();
1370 
1371 	std::string file;
1372 	bool found = false;
1373 	if (dsq->mod.isActive())
1374 	{
1375 		file = dsq->mod.getPath() + "eats.txt";
1376 		if(exists(file))
1377 			found = true;
1378 	}
1379 
1380 	if(!found)
1381 		file = "data/eats.txt";
1382 
1383 	InStream inf(file.c_str());
1384 
1385 	EatData curData;
1386 	std::string read;
1387 	while (inf >> read)
1388 	{
1389 		if (read.find(':')!=std::string::npos)
1390 		{
1391 			if (!curData.name.empty())
1392 			{
1393 				eats.push_back(curData);
1394 				debugLog("added eats: " + curData.name);
1395 			}
1396 			std::string name = read.substr(1, read.length());
1397 			EatData e;
1398 			curData = e;
1399 			curData.name = name;
1400 		}
1401 		else
1402 		{
1403 			if (!read.empty())
1404 			{
1405 				std::string eq, data;
1406 				inf >> eq;
1407 				std::getline(inf, data);
1408 				std::istringstream is(data);
1409 				if (read == "Shot")
1410 				{
1411 					is >> curData.shot;
1412 				}
1413 				else if (read == "AmmoUnitSize")
1414 				{
1415 					is >> curData.ammoUnitSize;
1416 					curData.ammo = curData.ammoUnitSize;
1417 				}
1418 				else if (read == "GetUnits")
1419 				{
1420 					is >> curData.getUnits;
1421 				}
1422 				else if (read == "Health")
1423 				{
1424 					is >> curData.health;
1425 				}
1426 			}
1427 		}
1428 	}
1429 	inf.close();
1430 }
1431 
hasLi()1432 bool Continuity::hasLi()
1433 {
1434 	return (getFlag(FLAG_LI) == 100);
1435 }
1436 
getSongNameBySlot(int slot)1437 std::string Continuity::getSongNameBySlot(int slot)
1438 {
1439 	return songSlotNames[slot];
1440 }
1441 
toggleLiCombat(bool t)1442 void Continuity::toggleLiCombat(bool t)
1443 {
1444 	if (hasLi())
1445 	{
1446 		setFlag(FLAG_LICOMBAT, (int)t);
1447 		if (dsq->game->li)
1448 		{
1449 			dsq->game->li->message("c", 0);
1450 		}
1451 	}
1452 }
1453 
warpLiToAvatar()1454 void Continuity::warpLiToAvatar()
1455 {
1456 	if (hasLi())
1457 	{
1458 		if (dsq->game && dsq->game->li && dsq->game->avatar)
1459 			dsq->game->li->position = dsq->game->avatar->position - Vector(0,-1);
1460 	}
1461 }
1462 
getSongByIndex(int idx)1463 Song *Continuity::getSongByIndex(int idx)
1464 {
1465 	return &songBank[idx];
1466 }
1467 
castSong(int num)1468 void Continuity::castSong(int num)
1469 {
1470 	if (!dsq->continuity.hasSong((SongType)num)) return;
1471 	Entity *selected = dsq->game->avatar;
1472 
1473 	Song *song = getSongByIndex(num);
1474 	if (!song)
1475 	{
1476 		std::ostringstream os;
1477 		os << "Could not find song with index [" << num << "]";
1478 		debugLog(os.str());
1479 	}
1480 	//float et = 0.5;
1481 	//float et = 10;
1482 	float et = 0.5;
1483 	std::ostringstream os;
1484 	os << "Song/SongSlot-" << dsq->continuity.getSongSlotByType(num);
1485 	PauseQuad *effect = new PauseQuad();
1486 	effect->pauseLevel = 1;
1487 	effect->setTexture(os.str());
1488 	effect->position = selected->position + selected->offset;
1489 	effect->scale.interpolateTo(Vector(3,3), et);
1490 	//effect->setBlendType(RenderObject::BLEND_ADD);
1491 	effect->alpha.ensureData();
1492 	effect->alpha.data->path.addPathNode(0, 0);
1493 	effect->alpha.data->path.addPathNode(0.5, 0.1);
1494 	effect->alpha.data->path.addPathNode(1, 0.5);
1495 	effect->alpha.data->path.addPathNode(0, 0.9);
1496 	effect->alpha.data->path.addPathNode(0, 1);
1497 	effect->alpha.startPath(et);
1498 	effect->setLife(et+0.1f);
1499 	effect->setDecayRate(1);
1500 	effect->setPositionSnapTo(&dsq->game->avatar->position);
1501 	dsq->game->addRenderObject(effect, LR_PARTICLES);
1502 
1503 
1504 	// song->script == 0: internal handler only
1505 	// song->script == 1: script handler only
1506 	// song->script == 2: both
1507 	if (song->script)
1508 	{
1509 		if (dsq->mod.isActive())
1510 			dsq->runScriptNum(dsq->mod.getPath() + "scripts/songs.lua", "castSong", num);
1511 		else
1512 			dsq->runScriptNum("songs.lua", "castSong", num);
1513 	}
1514 
1515 	if (song->script != 1)
1516 	{
1517 		switch((SongType)num)
1518 		{
1519 		case SONG_SHIELDAURA:
1520 			dsq->game->avatar->doShieldSong();
1521 		break;
1522 		case SONG_BIND:
1523 			dsq->game->avatar->doBindSong();
1524 		break;
1525 		case SONG_ENERGYFORM:
1526 			dsq->game->avatar->changeForm(FORM_ENERGY);
1527 		break;
1528 #ifndef AQUARIA_DEMO
1529 		case SONG_MAP:
1530 			if (dsq->game->autoMap)
1531 				dsq->game->autoMap->toggle(true);
1532 		break;
1533 		case SONG_HEAL:
1534 
1535 			// do heal effects
1536 			sound->playSfx("Heal");
1537 			selected->heal(2);
1538 
1539 			/*
1540 			Wynia *wynia = new Wynia;
1541 			wynia->trackTo(selected);
1542 			wynia->position = selected->position;
1543 			core->getTopStateData()->addRenderObject(wynia, PROJECTILES);
1544 			*/
1545 			selected->skeletalSprite.animate("healSelf", 0, 1);
1546 		break;
1547 		case SONG_TIME:
1548 		{
1549 			float v = 0.3;
1550 			dsq->gameSpeed.ensureData();
1551 			dsq->gameSpeed.data->path.clear();
1552 			dsq->gameSpeed.data->path.addPathNode(0,0);
1553 			dsq->gameSpeed.data->path.addPathNode(v,0.05);
1554 			dsq->gameSpeed.data->path.addPathNode(v,0.95);
1555 			dsq->gameSpeed.data->path.addPathNode(1,1.0);
1556 			dsq->gameSpeed.startPath(10);
1557 		}
1558 		break;
1559 		case SONG_LANCE:
1560 		{
1561 			Entity *e = dsq->game->getNearestEntity(dsq->game->avatar->position, 256, dsq->game->avatar, ET_ENEMY, DT_AVATAR_LANCEATTACH);
1562 			if (e)
1563 			{
1564 				e->attachLance();
1565 			}
1566 		}
1567 		break;
1568 		case SONG_LI:
1569 			if (!dsq->continuity.hasLi() && dsq->continuity.getFlag(FLAG_LI) > 100)
1570 			{
1571 				dsq->emote.playSfx(EMOTE_NAIJASADSIGH);
1572 			}
1573 			// HACK: when you first get li, the li pointer won't be set
1574 			if (dsq->game->li && dsq->game->avatar->isUnderWater() && dsq->continuity.hasLi())
1575 			{
1576 				if (!dsq->game->avatar->isNearObstruction(2) && !dsq->game->avatar->state.lockedToWall && !(dsq->game->li->position - dsq->game->avatar->position).isLength2DIn(400))
1577 				{
1578 					//dsq->game->avatar->disableInput();
1579 					dsq->overlay->color = Vector(1,1,1);
1580 					dsq->fade(1, 0.3);
1581 					dsq->main(0.3);
1582 					warpLiToAvatar();
1583 					dsq->fade(0, 0.3);
1584 					dsq->main(0.3);
1585 					dsq->overlay->color = 0;
1586 					//dsq->game->avatar->enableInput();
1587 				}
1588 				else if ((dsq->game->li->position - dsq->game->avatar->position).isLength2DIn(500))
1589 				{
1590 					if (dsq->continuity.getFlag(FLAG_LICOMBAT) == 1)
1591 						dsq->continuity.setFlag(FLAG_LICOMBAT, 0);
1592 					else
1593 						dsq->continuity.setFlag(FLAG_LICOMBAT, 1);
1594 					dsq->game->li->message("c", 0);
1595 				}
1596 				else
1597 				{
1598 					core->sound->playSfx("Denied");
1599 				}
1600 				/*
1601 				}
1602 				else
1603 				{
1604 					core->sound->playSfx("SongFail");
1605 				}
1606 				*/
1607 			}
1608 			else
1609 			{
1610 				core->sound->playSfx("Denied");
1611 			}
1612 		break;
1613 		case SONG_SPIRITFORM:
1614 			if (dsq->game->avatar->isUnderWater())
1615 			{
1616 				// Don't try to enter spirit form while warping,
1617 				// or we'll get stuck in the spirit world afterward.
1618 				bool inWarp = false;
1619 				const Vector avatarPosition(dsq->game->avatar->position);
1620 				for (Path *p = dsq->game->getFirstPathOfType(PATH_WARP); p; p = p->nextOfType)
1621 				{
1622 					if (p->isCoordinateInside(avatarPosition))
1623 					{
1624 						inWarp = true;
1625 						break;
1626 					}
1627 				}
1628 				if (inWarp)
1629 					core->sound->playSfx("SongFail");
1630 				else
1631 					dsq->game->avatar->changeForm(FORM_SPIRIT);
1632 			}
1633 			else
1634 			{
1635 				core->sound->playSfx("SongFail");
1636 			}
1637 		break;
1638 		case SONG_NATUREFORM:
1639 			dsq->game->avatar->changeForm(FORM_NATURE);
1640 		break;
1641 		case SONG_BEASTFORM:
1642 			dsq->game->avatar->changeForm(FORM_BEAST);
1643 		break;
1644 		case SONG_DUALFORM:
1645 			dsq->game->avatar->changeForm(FORM_DUAL);
1646 		break;
1647 		case SONG_SUNFORM:
1648 			dsq->game->avatar->changeForm(FORM_SUN);
1649 		break;
1650 		case SONG_FISHFORM:
1651 			dsq->game->avatar->changeForm(FORM_FISH);
1652 		break;
1653 		case SONG_SONGDOOR1:
1654 		break;
1655 #endif
1656 		}
1657 	}
1658 
1659 	FOR_ENTITIES(i)
1660 	{
1661 		Entity *e = *i;
1662 		if ((e->position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
1663 		{
1664 			e->song((SongType)num);
1665 		}
1666 	}
1667 	for (int i = 0; i < dsq->game->getNumPaths(); i++)
1668 	{
1669 		Path *p = dsq->game->getPath(i);
1670 		if (p && !p->nodes.empty())
1671 		{
1672 			PathNode *n = &p->nodes[0];
1673 			if ((n->position - dsq->game->avatar->position).isLength2DIn(1000))
1674 			{
1675 				p->song((SongType)num);
1676 			}
1677 		}
1678 	}
1679 }
1680 
setCostume(const std::string & c)1681 void Continuity::setCostume(const std::string &c)
1682 {
1683 	costume = c;
1684 	dsq->game->avatar->changeForm(FORM_NORMAL, false);
1685 }
1686 
learnSong(int song)1687 void Continuity::learnSong(int song)
1688 {
1689 	knowsSong[song] = true;
1690 }
1691 
unlearnSong(int song)1692 void Continuity::unlearnSong(int song)
1693 {
1694 	knowsSong[song] = false;
1695 }
1696 
isSongTypeForm(SongType s)1697 bool Continuity::isSongTypeForm(SongType s)
1698 {
1699 	return (s == SONG_ENERGYFORM || s == SONG_BEASTFORM || s == SONG_NATUREFORM || s == SONG_SUNFORM || s == SONG_SPIRITFORM || s == SONG_FISHFORM || s== SONG_DUALFORM);
1700 }
1701 
shortenSong(Song & song,int size)1702 void Continuity::shortenSong(Song &song, int size)
1703 {
1704 	if (song.notes.size() > size)
1705 	{
1706 		Song copy = song;
1707 		song.notes.clear();
1708 		for (int i = copy.notes.size()-size; i < copy.notes.size(); i++)
1709 		{
1710 			song.notes.push_back(copy.notes[i]);
1711 		}
1712 	}
1713 }
1714 
1715 struct SongCheck
1716 {
SongCheckSongCheck1717 	SongCheck(int idx, Song *s)
1718 	{ rank = 0; pass = false; this->song = s; songIdx = idx; }
1719 	int songIdx;
1720 	int rank;
1721 	bool pass;
1722 	Song *song;
1723 };
1724 
1725 const int songTolerance = 4;
1726 
checkSongAssisted(const Song & s)1727 int Continuity::checkSongAssisted(const Song &s)
1728 {
1729 	// shorten song
1730 	Song song = s;
1731 	shortenSong(song, 64);
1732 
1733 	std::vector<SongCheck> songChecks;
1734 	for (int c = 0; c < songBank.size(); c++)
1735 	{
1736 		int i = songSlotsToType[c];
1737 		if (knowsSong[i])
1738 		{
1739 			Song *s = &songBank[i];
1740 			songChecks.push_back(SongCheck(i, s));
1741 		}
1742 	}
1743 	for (int i = 0; i < songChecks.size(); i++)
1744 	{
1745 		int j=0,c=0,m=0,last=0,rank=0;
1746 		int ms=songChecks[i].song->notes.size();
1747 		j = 0;
1748 
1749 loop:
1750 		rank = 0;
1751 		last = j;
1752 		m = 0;
1753 		for (c = 0; c < ms; c++)
1754 		{
1755 			while (j < song.notes.size() && (*songChecks[i].song).notes[c] != song.notes[j])
1756 			{
1757 				j++;
1758 				if (j >= song.notes.size())
1759 					break;
1760 			}
1761 			if (j < song.notes.size())
1762 			{
1763 				if (m == 0)
1764 					last = j-1;
1765 
1766 				int diff = j-last;
1767 				if (diff < 0)
1768 					diff = 1;
1769 
1770 				if (diff >= songTolerance)
1771 					break;
1772 
1773 				m++;
1774 
1775 				rank += diff;
1776 				last=j;
1777 			}
1778 			else
1779 			{
1780 				break;
1781 			}
1782 		}
1783 		if (m == ms)
1784 		{
1785 			// make sure last note is more or less close
1786 			if (song.notes.size()-last < 2)
1787 			{
1788 				//rank += song.size()-last;
1789 				/*
1790 				std::ostringstream os;
1791 				os << "songCheck: " << songChecks[i].songIdx << " completed with rank " << rank;
1792 				debugLog(os.str());
1793 				*/
1794 
1795 				songChecks[i].pass = true;
1796 				songChecks[i].rank = rank;
1797 			}
1798 		}
1799 		if (j < song.notes.size())
1800 			goto loop;
1801 	}
1802 	int songIdx = SONG_NONE, lowestRank = -1;
1803 	for (int i = 0; i < songChecks.size(); i++)
1804 	{
1805 		if (songChecks[i].pass)
1806 		{
1807 			int checkRank = songChecks[i].rank;
1808 			if (lowestRank == -1 || checkRank < lowestRank)
1809 			{
1810 				lowestRank = songChecks[i].rank;
1811 				songIdx = songChecks[i].songIdx;
1812 			}
1813 		}
1814 	}
1815 
1816 	/*
1817 	std::ostringstream os;
1818 	os << "lowest rank: " << lowestRank;
1819 	debugLog(os.str());
1820 	*/
1821 
1822 	return songIdx;
1823 }
1824 
checkSong(const Song & song)1825 int Continuity::checkSong(const Song &song)
1826 {
1827 	bool knowAllSongs = false;
1828 	// way too long song
1829 	if (song.notes.size() > 64) return SONG_NONE;
1830 	for (int c = 0; c < songBank.size(); c++)
1831 	{
1832 		int i = songSlotsToType[c];
1833 		if ((knowAllSongs || knowsSong[i]))
1834 		{
1835 			Song *s = &songBank[i];
1836 			if (s->notes.empty()) continue;
1837 			int j = 0;
1838 			//if (s->size() == song.size())
1839 			{
1840 				bool foundSong = false;
1841 				int currentNote = 0;
1842 				for (j = 0; j < song.notes.size(); j++)
1843 				{
1844 					if (currentNote >= 0 && currentNote < (*s).notes.size())
1845 					{
1846 						int bankNote = (*s).notes[currentNote];
1847 						int songNote = song.notes[j];
1848 						if (bankNote == songNote)
1849 						{
1850 							currentNote++;
1851 						}
1852 						else
1853 							currentNote = 0;
1854 
1855 						if (currentNote == s->notes.size())
1856 						{
1857 							if (j == song.notes.size()-1)
1858 							{
1859 								foundSong = true;
1860 								break;
1861 							}
1862 							else
1863 							{
1864 								currentNote = 0;
1865 							}
1866 						}
1867 					}
1868 				}
1869 				if (j != song.notes.size()-1) foundSong = false;
1870 				//if (j == s->size())
1871 				if (foundSong)
1872 				{
1873 					return i;
1874 				}
1875 			}
1876 		}
1877 	}
1878 	return -1;
1879 }
1880 
getHoursMinutesSeconds(int * hours,int * minutes,int * seconds)1881 void Continuity::getHoursMinutesSeconds(int *hours, int *minutes, int *seconds)
1882 {
1883 	(*hours) = int(this->seconds/(60*60));
1884 	(*minutes) = int((this->seconds/60) - ((*hours)*60));
1885 	(*seconds) = this->seconds - (*minutes)*60 - (*hours)*60*60;
1886 }
1887 
hasSong(int song)1888 bool Continuity::hasSong(int song)
1889 {
1890 	return knowsSong[song];
1891 }
1892 
getIngredientGfx(const std::string & name)1893 std::string Continuity::getIngredientGfx(const std::string &name)
1894 {
1895 	IngredientData *i = getIngredientDataByName(name);
1896 	if (i)
1897 	{
1898 		return i->gfx;
1899 	}
1900 	return "";
1901 }
1902 
update(float dt)1903 void Continuity::update(float dt)
1904 {
1905 	if (dsq->game->isActive())
1906 		seconds += dt;
1907 
1908 	if (statsAndAchievements) {
1909 		statsAndAchievements->update(dt);
1910 		statsAndAchievements->RunFrame();
1911 	}
1912 
1913 	if (dsq->game->isActive() && !dsq->game->isPaused() /*&& !(getWorldType() == WT_SPIRIT)*/)
1914 	{
1915 
1916 		if (liPowerTimer.updateCheck(dt))
1917 		{
1918 			liPower = 0;
1919 		}
1920 
1921 		if (speedMultTimer.updateCheck(dt))
1922 		{
1923 			speedMult = 1;
1924 		}
1925 
1926 		if (lightTimer.updateCheck(dt))
1927 		{
1928 			light = 0;
1929 		}
1930 
1931 		if (petPowerTimer.updateCheck(dt))
1932 		{
1933 			petPower = 0;
1934 		}
1935 
1936 		if (dsq->game->avatar && dsq->game->avatar->isInputEnabled())
1937 		{
1938 			if (poisonTimer.updateCheck(dt))
1939 			{
1940 				poison = 0;
1941 			}
1942 
1943 			if (poison)
1944 			{
1945 				if (poisonBitTimer.updateCheck(dt))
1946 				{
1947 					poisonBitTimer.start(poisonBitTimeAvatar);
1948 					if (dsq->game->avatar)
1949 					{
1950 						core->sound->playSfx("poison");
1951 
1952 						DamageData d;
1953 						d.damage = poison * 0.2f;
1954 						d.useTimer = 0;
1955 						d.damageType = DT_ENEMY_ACTIVEPOISON;
1956 						dsq->game->avatar->damage(d);
1957 
1958 						dsq->spawnParticleEffect("PoisonBubbles", dsq->game->avatar->position);
1959 					}
1960 				}
1961 			}
1962 
1963 			if (webTimer.updateCheck(dt))
1964 			{
1965 				dsq->game->avatar->clearWeb();
1966 			}
1967 
1968 			if (dsq->game->avatar->web)
1969 			{
1970 				if (webBitTimer.updateCheck(dt))
1971 				{
1972 					webBitTimer.start(webBitTime);
1973 
1974 					dsq->game->avatar->web->addPoint(dsq->game->avatar->position);
1975 				}
1976 			}
1977 		}
1978 
1979 		if (energyTimer.updateCheck(dt))
1980 		{
1981 			energyMult = 0;
1982 		}
1983 
1984 		if (defenseMultTimer.updateCheck(dt))
1985 		{
1986 			defenseMult = 1;
1987 		}
1988 
1989 		if (biteMultTimer.updateCheck(dt))
1990 		{
1991 			biteMult = 1;
1992 		}
1993 
1994 		if (fishPoisonTimer.updateCheck(dt))
1995 		{
1996 			fishPoison = 1;
1997 		}
1998 
1999 		if (tripTimer.updateCheck(dt))
2000 		{
2001 			dsq->game->avatar->removeTripEffects();
2002 		}
2003 
2004 		if (regenTimer.updateCheck(dt))
2005 		{
2006 		}
2007 
2008 		if (invincibleTimer.updateCheck(dt))
2009 		{
2010 		}
2011 
2012 		if (regenTimer.isActive())
2013 		{
2014 			/*
2015 			static float regenBit = 0;
2016 			regenBit += dt;
2017 			if (regenBit > 1)
2018 			*/
2019 			{
2020 				Avatar *a = dsq->game->avatar;
2021 				if (a)
2022 				{
2023 					a->heal(dt*0.5f);
2024 				}
2025 				//regenBit = 0;
2026 			}
2027 		}
2028 	}
2029 
2030 
2031 }
2032 
shiftWorlds()2033 void Continuity::shiftWorlds()
2034 {
2035 	WorldType lastWorld = worldType;
2036 	if (worldType == WT_NORMAL)
2037 	{
2038 		worldType = WT_SPIRIT;
2039 		dsq->game->setWorldPaused(true);
2040 	}
2041 	else if (worldType == WT_SPIRIT)
2042 	{
2043 		worldType = WT_NORMAL;
2044 		dsq->game->setWorldPaused(false);
2045 	}
2046 	FOR_ENTITIES(i)
2047 	{
2048 		Entity *e = *i;
2049 		e->shiftWorlds(lastWorld, worldType);
2050 	}
2051 	applyWorldEffects(worldType, 1, 1);
2052 	if (worldType == WT_SPIRIT)
2053 		core->sound->playSfx("Spirit-Enter");
2054 	else if (worldType == WT_NORMAL)
2055 		core->sound->playSfx("Spirit-Return");
2056 }
2057 
getBeaconByIndex(int index)2058 BeaconData *Continuity::getBeaconByIndex(int index)
2059 {
2060 	for (Beacons::iterator i = beacons.begin(); i != beacons.end(); i++)
2061 	{
2062 		if ((*i).index == index)
2063 		{
2064 			return &(*i); // stupidity
2065 		}
2066 	}
2067 	return 0;
2068 }
2069 
setBeacon(int index,bool on,Vector pos,Vector color)2070 void Continuity::setBeacon(int index, bool on, Vector pos, Vector color)
2071 {
2072 	if (on)
2073 	{
2074 		BeaconData *b = getBeaconByIndex(index);
2075 		if (!b)
2076 		{
2077 			BeaconData newb;
2078 			newb.index = index;
2079 			beacons.push_back(newb);
2080 			b = getBeaconByIndex(index);
2081 		}
2082 		b->on = true;
2083 		b->pos = pos;
2084 		b->color = color;
2085 	}
2086 	else
2087 	{
2088 		BeaconData *b = getBeaconByIndex(index);
2089 		if (b)
2090 		{
2091 			b->on = false;
2092 		}
2093 	}
2094 }
2095 
applyWorldEffects(WorldType type,bool transition,bool affectMusic)2096 void Continuity::applyWorldEffects(WorldType type, bool transition, bool affectMusic)
2097 {
2098 	float time = 1;
2099 	if (!transition) time = 0;
2100 	if (type == WT_SPIRIT)
2101 	{
2102 
2103 		if(dsq->user.video.blur)
2104 		{
2105 			core->postProcessingFx.blendType = 1;
2106 			core->postProcessingFx.intensity = 0.2f;
2107 			core->postProcessingFx.layer = LR_AFTER_EFFECTS;//LR_AFTER_EFFECTS;
2108 			core->postProcessingFx.renderLayer = LR_AFTER_EFFECTS;
2109 			core->postProcessingFx.enable(FXT_RADIALBLUR);
2110 		}
2111 
2112 		dsq->game->avatar->canWarp = false;
2113 
2114 		/*
2115 		if (affectMusic)
2116 			dsq->sound->toggleEffects(1);
2117 		*/
2118 		dsq->game->backupSceneColor = dsq->game->sceneColor;
2119 		dsq->game->sceneColor.interpolateTo(Vector(0.4, 0.8, 0.9), time);
2120 		dsq->game->avatar->applyWorldEffects(type);
2121 	}
2122 	else
2123 	{
2124 		dsq->game->avatar->canWarp = true;
2125 
2126 		core->postProcessingFx.disable(FXT_RADIALBLUR);
2127 		//worldType = WT_SPIRIT;
2128 		/*
2129 		if (affectMusic)
2130 			dsq->sound->toggleEffects(0);
2131 		*/
2132 		//dsq->game->sceneColor.interpolateTo(dsq->game->backupSceneColor, time);
2133 		dsq->game->sceneColor.interpolateTo(Vector(1,1,1), time);
2134 		dsq->game->avatar->applyWorldEffects(type);
2135 	}
2136 	if (time > 0)
2137 	{
2138 		/*
2139 		dsq->game->avatar->slowToRest();
2140 		dsq->game->avatar->disableInput();
2141 		core->main(time);
2142 		dsq->game->avatar->enableInput();
2143 		*/
2144 	}
2145 	worldType = type;
2146 }
2147 
eatBeast(const EatData & eatData)2148 void Continuity::eatBeast(const EatData &eatData)
2149 {
2150 	if (!eatData.name.empty())
2151 	{
2152 		for (int i = 0; i < eatData.getUnits; i++)
2153 		{
2154 			if (naijaEats.size() < MAX_EATS)
2155 			{
2156 				if (!eatData.shot.empty())
2157 					naijaEats.push_back(eatData);
2158 			}
2159 		}
2160 
2161 		if (eatData.health > 0)
2162 		{
2163 			dsq->game->avatar->heal(eatData.health);
2164 		}
2165 	}
2166 }
2167 
removeNaijaEat(int idx)2168 void Continuity::removeNaijaEat(int idx)
2169 {
2170 	std::vector<EatData> copy = naijaEats;
2171 	naijaEats.clear();
2172 	for (int i = 0; i < copy.size(); i++)
2173 	{
2174 		if (i != idx)
2175 			naijaEats.push_back(copy[i]);
2176 	}
2177 }
2178 
removeLastNaijaEat()2179 void Continuity::removeLastNaijaEat()
2180 {
2181 	removeNaijaEat(naijaEats.size()-1);
2182 }
2183 
getLastNaijaEat()2184 EatData *Continuity::getLastNaijaEat()
2185 {
2186 	if (naijaEats.empty())
2187 		return 0;
2188 	return &naijaEats[naijaEats.size()-1];
2189 }
2190 
isNaijaEatsEmpty()2191 bool Continuity::isNaijaEatsEmpty()
2192 {
2193 	return naijaEats.empty();
2194 }
2195 
init()2196 void Continuity::init()
2197 {
2198 	statsAndAchievements = new StatsAndAchievements;
2199 }
2200 
shutdown()2201 void Continuity::shutdown()
2202 {
2203 	if (statsAndAchievements)
2204 	{
2205 		delete statsAndAchievements;
2206 		statsAndAchievements = 0;
2207 	}
2208 }
2209 
initAvatar(Avatar * a)2210 void Continuity::initAvatar(Avatar *a)
2211 {
2212 	debugLog("in initAvatar");
2213 	a->maxHealth = maxHealth;
2214 	a->health = 0;
2215 	a->heal(health);
2216 
2217 	// block spirit form in case of bug
2218 	if (form == FORM_SPIRIT)
2219 		form = FORM_NORMAL;
2220 
2221 	debugLog("changeForm...");
2222 	a->changeForm(form, false, true, FORM_NORMAL);
2223 	debugLog("done");
2224 
2225 	debugLog("auraType...");
2226 	if (auraType != AURA_NONE && auraTimer > 0)
2227 	{
2228 		a->activateAura(auraType);
2229 		a->auraTimer = auraTimer;
2230 	}
2231 
2232 	debugLog("trip");
2233 	if (tripTimer.isActive())
2234 	{
2235 		a->applyTripEffects();
2236 	}
2237 
2238 	debugLog("web");
2239 	if (webTimer.isActive())
2240 	{
2241 		setWeb(webTimer.getValue());
2242 	}
2243 	debugLog("done initAvatar");
2244 
2245 	// HACK-ish
2246 	a->skeletalSprite.stopAllAnimations();
2247 	a->skeletalSprite.animate(a->getIdleAnimName(), -1, 0);
2248 }
2249 
spawnAllIngredients(const Vector & position)2250 void Continuity::spawnAllIngredients(const Vector &position)
2251 {
2252 	for (int i = 0; i < ingredientData.size(); i++)
2253 	{
2254 		dsq->game->spawnIngredient(ingredientData[i]->name, position, 4, 0);
2255 	}
2256 }
2257 
removeEmptyIngredients()2258 void Continuity::removeEmptyIngredients()
2259 {
2260 	for (IngredientDatas::iterator i = ingredients.begin(); i != ingredients.end();)
2261 	{
2262 		IngredientData *data = *i;
2263 		if (data->amount == 0 && data->held <= 0)
2264 		{
2265 			i = ingredients.erase(i);
2266 		}
2267 		else
2268 		{
2269 			++ i;
2270 		}
2271 	}
2272 }
2273 
refreshAvatarData(Avatar * a)2274 void Continuity::refreshAvatarData(Avatar *a)
2275 {
2276 	maxHealth = a->maxHealth;
2277 	health = a->health;
2278 	auraType = a->activeAura;
2279 	auraTimer = a->auraTimer;
2280 }
2281 
getFlag(std::string flag)2282 int Continuity::getFlag(std::string flag)
2283 {
2284 	if (flag == "story")
2285 		errorLog("Hey! Use the new fancy story functions!");
2286 	return flags[flag];
2287 }
2288 
setFlag(std::string flag,int v)2289 void Continuity::setFlag(std::string flag, int v)
2290 {
2291 	flags[flag] = v;
2292 }
2293 
2294 /*
2295 void Continuity::setActivePet(int flag)
2296 {
2297 	setFlag(FLAG_ACTIVEPET, flag);
2298 }
2299 */
2300 
loadPetData()2301 void Continuity::loadPetData()
2302 {
2303 	petData.clear();
2304 	InStream in("data/pets.txt");
2305 	std::string read;
2306 	while (std::getline(in, read))
2307 	{
2308 		int num=0;
2309 		PetData p;
2310 		std::istringstream is(read);
2311 		is >> num >> p.namePart;
2312 		petData.push_back(p);
2313 	}
2314 	in.close();
2315 }
2316 
getPetData(int idx)2317 PetData *Continuity::getPetData(int idx)
2318 {
2319 	if (idx < 0 || idx >= petData.size())
2320 	{
2321 		std::ostringstream os;
2322 		os << "getPetData(" << idx << ") index out of range";
2323 		debugLog(os.str());
2324 		return 0;
2325 
2326 	}
2327 
2328 	return &petData[idx];
2329 }
2330 
isStory(float v)2331 bool Continuity::isStory(float v)
2332 {
2333 	return (story == v);
2334 }
2335 
getStory()2336 float Continuity::getStory()
2337 {
2338 	return story;
2339 }
2340 
setStory(float v)2341 void Continuity::setStory(float v)
2342 {
2343 	story = v;
2344 }
2345 
getStringFlag(std::string flag)2346 std::string Continuity::getStringFlag(std::string flag)
2347 {
2348 	return stringFlags[flag];
2349 }
2350 
setStringFlag(std::string flag,std::string v)2351 void Continuity::setStringFlag(std::string flag, std::string v)
2352 {
2353 	stringFlags[flag] = v;
2354 }
2355 
clearTempFlags()2356 void Continuity::clearTempFlags()
2357 {
2358 	for (Flags::iterator i = flags.begin(); i != flags.end(); i++)
2359 	{
2360 		if ((*i).first.find("CHOICE_")!=std::string::npos)
2361 		{
2362 			(*i).second = 0;
2363 		}
2364 	}
2365 }
2366 
upgradeHealth()2367 void Continuity::upgradeHealth()
2368 {
2369 	Avatar *a = dsq->game->avatar;
2370 	maxHealth = a->maxHealth+1;
2371 	a->maxHealth = maxHealth;
2372 	a->heal(maxHealth - a->health);
2373 }
2374 
saveFile(int slot,Vector position,unsigned char * scrShotData,int scrShotWidth,int scrShotHeight)2375 void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, int scrShotWidth, int scrShotHeight)
2376 {
2377 	refreshAvatarData(dsq->game->avatar);
2378 
2379 	if (position.isZero())
2380 	{
2381 		position = dsq->game->avatar->position;
2382 	}
2383 
2384 	dsq->user.save();
2385 
2386 	XMLDocument doc;
2387 
2388 	XMLElement *version = doc.NewElement("Version");
2389 	{
2390 		version->SetAttribute("major",		VERSION_MAJOR);
2391 		version->SetAttribute("minor",		VERSION_MINOR);
2392 		version->SetAttribute("revision",	VERSION_REVISION);
2393 	}
2394 	doc.InsertEndChild(version);
2395 
2396 	for (Flags::iterator i = flags.begin(); i != flags.end(); i++)
2397 	{
2398 		if ((*i).first.find("CHOICE_")!=std::string::npos) continue;
2399 		if ((*i).first.find("TEMP_")!=std::string::npos) continue;
2400 		XMLElement *flag = doc.NewElement("Flag");
2401 		flag->SetAttribute("name", (*i).first.c_str());
2402 		flag->SetAttribute("value", (*i).second);
2403 		doc.InsertEndChild(flag);
2404 	}
2405 
2406 	XMLElement *efx = doc.NewElement("EFX");
2407 	{
2408 		std::ostringstream os;
2409 		for (EntityFlags::iterator i = entityFlags.begin(); i != entityFlags.end(); i++)
2410 		{
2411 			os << (*i).first << " " << (*i).second << " ";
2412 		}
2413 		efx->SetAttribute("a", os.str().c_str());
2414 	}
2415 	doc.InsertEndChild(efx);
2416 
2417 	XMLElement *gems = doc.NewElement("Gems");
2418 	{
2419 		std::ostringstream os;
2420 		bool hasUserString = false;
2421 		os << this->gems.size() << " ";
2422 		for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++)
2423 		{
2424 			os << (*i).name << " " << (*i).pos.x << " " << (*i).pos.y << " ";
2425 			os << (*i).canMove << " ";
2426 
2427 			hasUserString = !(*i).userString.empty();
2428 
2429 			os << hasUserString << " ";
2430 
2431 			if (hasUserString)
2432 				os << spacesToUnderscores((*i).userString) << " ";
2433 		}
2434 		gems->SetAttribute("c", os.str().c_str());
2435 
2436 		// This is the format used in the android version. Keeping this commented out for now,
2437 		// but it should be used instead of the code above in some time -- FG
2438 		/*
2439 		os.str("");
2440 		bool hasMapName = false;
2441 		os << this->gems.size() << " ";
2442 		for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++)
2443 		{
2444 			os << (*i).name << " ";
2445 			hasMapName = !(*i).mapName.empty();
2446 			os << hasMapName << " ";
2447 			if(hasMapName)
2448 				os << (*i).mapName << " "; // warning: this will fail to load if the map name contains whitespace
2449 
2450 			os << (*i).pos.x << " " << (*i).pos.y << " ";
2451 			os << (*i).canMove << " ";
2452 
2453 			hasUserString = !(*i).userString.empty();
2454 			os << hasUserString << " ";
2455 			if (hasUserString)
2456 				os << spacesToUnderscores((*i).userString) << " ";
2457 		}
2458 		gems->SetAttribute("d", os.str());
2459 		*/
2460 
2461 	}
2462 	doc.InsertEndChild(gems);
2463 
2464 	XMLElement *worldMap = doc.NewElement("WorldMap");
2465 	{
2466 		std::ostringstream os;
2467 		for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
2468 		{
2469 			WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
2470 			if (tile->revealed)
2471 			{
2472 				os << tile->index << " ";
2473 			}
2474 		}
2475 		worldMap->SetAttribute("b", os.str().c_str());
2476 
2477 #ifdef AQUARIA_BUILD_MAPVIS
2478 		if (dsq->game->worldMapRender)
2479 		{
2480 			std::ostringstream os;
2481 			for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
2482 			{
2483 				WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
2484 				os << tile->index << " ";
2485 				tile->dataToString(os);
2486 				os << " ";
2487 			}
2488 			worldMap->SetAttribute("va", os.str().c_str());
2489 		}
2490 #endif
2491 	}
2492 	doc.InsertEndChild(worldMap);
2493 
2494 	XMLElement *vox = doc.NewElement("VO");
2495 	{
2496 		std::ostringstream os;
2497 		for (int i = 0; i < dsq->continuity.voiceOversPlayed.size(); i++)
2498 		{
2499 
2500 			os << dsq->continuity.voiceOversPlayed[i] << " ";
2501 		}
2502 		vox->SetAttribute("v", os.str().c_str());
2503 	}
2504 	doc.InsertEndChild(vox);
2505 
2506 	XMLElement *eats = doc.NewElement("eats");
2507 	{
2508 		std::ostringstream os;
2509 		int num = naijaEats.size();
2510 		os << num << " ";
2511 		for (int i = 0; i < num; i++)
2512 		{
2513 			EatData *eat = &naijaEats[i];
2514 			os << eat->name << " "  << eat->shot << " " << eat->ammo << " " << eat->ammoUnitSize << " ";
2515 		}
2516 		eats->SetAttribute("a", os.str().c_str());
2517 	}
2518 	doc.InsertEndChild(eats);
2519 
2520 	XMLElement *bcn = doc.NewElement("bcn");
2521 	{
2522 		std::ostringstream os;
2523 		for (Beacons::iterator i = beacons.begin(); i != beacons.end(); i++)
2524 		{
2525 			BeaconData *data = &(*i);
2526 			os << data->index << " " << data->on << " ";
2527 			os << data->color.x << " " << data->color.y << " " << data->color.z << " ";
2528 			os << data->pos.x << " " << data->pos.y << " " << data->pos.z << " ";
2529 		}
2530 		bcn->SetAttribute("a", os.str().c_str());
2531 	}
2532 	doc.InsertEndChild(bcn);
2533 
2534 	XMLElement *s = doc.NewElement("Story");
2535 	{
2536 		std::ostringstream os;
2537 		os << story;
2538 		s->SetAttribute("v", os.str().c_str());
2539 		doc.InsertEndChild(s);
2540 	}
2541 
2542 	for (StringFlags::iterator i = stringFlags.begin(); i != stringFlags.end(); i++)
2543 	{
2544 		if ((*i).first.find("TEMP_")!=std::string::npos) continue;
2545 		XMLElement *stringFlag = doc.NewElement("StringFlag");
2546 		stringFlag->SetAttribute("name", (*i).first.c_str());
2547 		stringFlag->SetAttribute("value", (*i).second.c_str());
2548 		doc.InsertEndChild(stringFlag);
2549 	}
2550 
2551 	XMLElement *startData = doc.NewElement("StartData");
2552 	startData->SetAttribute("x", int(position.x));
2553 	startData->SetAttribute("y", int(position.y));
2554 	startData->SetAttribute("scene", dsq->game->sceneName.c_str());
2555 	startData->SetAttribute("exp", dsq->continuity.exp);
2556 	startData->SetAttribute("h", dsq->continuity.maxHealth);
2557 	startData->SetAttribute("ch", dsq->continuity.health);
2558 	startData->SetAttribute("naijaModel", dsq->continuity.naijaModel.c_str());
2559 	startData->SetAttribute("costume", dsq->continuity.costume.c_str());
2560 	startData->SetAttribute("form", dsq->continuity.form);
2561 	if (dsq->mod.isActive())
2562 		startData->SetAttribute("mod", dsq->mod.getName().c_str());
2563 	std::ostringstream secondsOS;
2564 	secondsOS << dsq->continuity.seconds;
2565 	startData->SetAttribute("seconds", secondsOS.str().c_str());
2566 	std::ostringstream os2;
2567 	for (int i = 0; i < SONG_MAX; i++)
2568 	{
2569 		if (knowsSong[i])
2570 		{
2571 			os2 << i << " ";
2572 		}
2573 	}
2574 	startData->SetAttribute("songs", os2.str().c_str());
2575 
2576 	// new format as used by android version
2577 	std::ostringstream ingrNames;
2578 	for (int i = 0; i < ingredients.size(); i++)
2579 	{
2580 		IngredientData *data = ingredients[i];
2581 		ingrNames << data->name << " " << data->amount << " ";
2582 	}
2583 	startData->SetAttribute("ingrNames", ingrNames.str().c_str());
2584 
2585 	// for compatibility with older versions
2586 	std::ostringstream ingrOs;
2587 	for (int i = 0; i < ingredients.size(); i++)
2588 	{
2589 		IngredientData *data = ingredients[i];
2590 		ingrOs << data->getIndex() << " " << data->amount << " ";
2591 	}
2592 	startData->SetAttribute("ingr", ingrOs.str().c_str());
2593 
2594 	std::ostringstream recOs;
2595 	for (int i = 0; i < recipes.size(); i++)
2596 	{
2597 		recOs << recipes[i].isKnown() << " ";
2598 	}
2599 	startData->SetAttribute("rec", recOs.str().c_str());
2600 
2601 	std::ostringstream os3;
2602 	for (int i = 0; i < FORMUPGRADE_MAX; i++)
2603 	{
2604 		if (hasFormUpgrade((FormUpgradeType)i))
2605 		{
2606 			os3 << i << " ";
2607 		}
2608 	}
2609 	startData->SetAttribute("formUpgrades", os3.str().c_str());
2610 
2611 	std::ostringstream fos;
2612 	fos << MAX_FLAGS << " ";
2613 	for (int i = 0; i < MAX_FLAGS; i++)
2614 	{
2615 		fos << intFlags[i] << " ";
2616 	}
2617 	startData->SetAttribute("intFlags", fos.str().c_str());
2618 
2619 	// Additional data for the android version
2620 
2621 #define SINGLE_FLOAT_ATTR(name, cond, val) \
2622 	do { if((cond) && (val)) { \
2623 		std::ostringstream osf; \
2624 		osf << (val); \
2625 		startData->SetAttribute(name, osf.str().c_str()); \
2626 	}} while(0)
2627 
2628 	SINGLE_FLOAT_ATTR("blind", dsq->game->avatar->state.blind, dsq->game->avatar->state.blindTimer.getValue());
2629 	SINGLE_FLOAT_ATTR("invincible", invincibleTimer.isActive(), invincibleTimer.getValue());
2630 	SINGLE_FLOAT_ATTR("regen", regenTimer.isActive(), regenTimer.getValue());
2631 	SINGLE_FLOAT_ATTR("trip", tripTimer.isActive(), tripTimer.getValue());
2632 	SINGLE_FLOAT_ATTR("shieldPoints", true, dsq->game->avatar->shieldPoints);
2633 	SINGLE_FLOAT_ATTR("webTimer", webTimer.isActive(), webTimer.getValue()); // Extension; not present in the android version
2634 
2635 #undef SINGLE_FLOAT_ATTR
2636 
2637 #define TIMER_AND_VALUE_ATTR(name, timer, val) \
2638 	do { if(((timer).isActive()) && (val)) { \
2639 		std::ostringstream osf; \
2640 		osf << (val) << " " << ((timer).getValue()); \
2641 		startData->SetAttribute((name), osf.str().c_str()); \
2642 	}} while(0)
2643 
2644 	TIMER_AND_VALUE_ATTR("biteMult", biteMultTimer, biteMult);
2645 	TIMER_AND_VALUE_ATTR("speedMult", speedMultTimer, speedMult);
2646 	TIMER_AND_VALUE_ATTR("defenseMult", defenseMultTimer, defenseMult);
2647 	TIMER_AND_VALUE_ATTR("energyMult", energyTimer, energyMult);
2648 	TIMER_AND_VALUE_ATTR("petPower", petPowerTimer, petPower);
2649 	TIMER_AND_VALUE_ATTR("liPower", liPowerTimer, liPower);
2650 	TIMER_AND_VALUE_ATTR("light", lightTimer, light);
2651 
2652 #undef TIMER_AND_VALUE_ATTR
2653 
2654 	if(poisonTimer.isActive())
2655 	{
2656 		std::ostringstream osp;
2657 		osp << poison << " " << poisonTimer.getValue() << " " << poisonBitTimer.getValue();
2658 		startData->SetAttribute("poison", osp.str().c_str());
2659 	}
2660 
2661 	if(dsq->game->avatar->activeAura != AURA_NONE)
2662 	{
2663 		std::ostringstream osa;
2664 		osa << dsq->game->avatar->activeAura << " " << dsq->game->avatar->auraTimer;
2665 		startData->SetAttribute("aura", osa.str().c_str());
2666 	}
2667 
2668 	// FIXME: Web is a bit weird. There are 2 webBitTimer variables in use, one in Continuity, one in Avatar.
2669 	// Because the avatar one ticks every 0.5 seconds, it will be hardly noticeable if that timer is off.
2670 	// So we just use the Continuty timers and hope for the best. -- FG
2671 	if(webTimer.isActive() && dsq->game->avatar->web)
2672 	{
2673 		Web *w = dsq->game->avatar->web;
2674 		const int nump = w->getNumPoints();
2675 		std::ostringstream osw;
2676 		osw << webBitTimer.getValue() << " " << nump << " ";
2677 		for(int i = 0; i < nump; ++i)
2678 		{
2679 			Vector v = w->getPoint(i);
2680 			osw << v.x << " " << v.y << " ";
2681 		}
2682 		startData->SetAttribute("web", osw.str().c_str());
2683 	}
2684 
2685 	// end extra android data
2686 
2687 	doc.InsertEndChild(startData);
2688 
2689 
2690 	std::string fn = core->adjustFilenameCase(getSaveFileName(slot, "aqs"));
2691 	FILE *fh = fopen(fn.c_str(), "wb");
2692 	if(!fh)
2693 	{
2694 		debugLog("FAILED TO SAVE GAME");
2695 		return;
2696 	}
2697 
2698 	XMLPrinter printer;
2699 	doc.Accept( &printer );
2700 	const char* xmlstr = printer.CStr();
2701 	ZlibCompressor z;
2702 	z.init((void*)xmlstr, printer.CStrSize(), ZlibCompressor::REUSE);
2703 	z.SetForceCompression(true);
2704 	z.Compress(3);
2705 	std::ostringstream os;
2706 	os << "Writing " << z.size() << " bytes to save file " << fn;
2707 	debugLog(os.str());
2708 	size_t written = fwrite(z.contents(), 1, z.size(), fh);
2709 	if (written != z.size())
2710 	{
2711 		debugLog("FAILED TO WRITE SAVE FILE COMPLETELY");
2712 	}
2713 	fclose(fh);
2714 }
2715 
getSaveFileName(int slot,const std::string & pfix)2716 std::string Continuity::getSaveFileName(int slot, const std::string &pfix)
2717 {
2718 	std::ostringstream os;
2719 	os << dsq->getSaveDirectory() << "/" << dsq->currentProfile.name << "-" << numToZeroString(slot, 4) << "." << pfix;
2720 	return os.str();
2721 }
2722 
loadFileData(int slot,XMLDocument & doc)2723 void Continuity::loadFileData(int slot, XMLDocument &doc)
2724 {
2725 	std::string teh_file = dsq->continuity.getSaveFileName(slot, "aqs");
2726 	if(!exists(teh_file))
2727 		teh_file = dsq->continuity.getSaveFileName(slot, "bin");
2728 
2729 	if (exists(teh_file))
2730 	{
2731 		unsigned long size = 0;
2732 		char *buf = readCompressedFile(teh_file, &size);
2733 		if (!buf)
2734 		{
2735 			errorLog("Failed to decompress save file: " + teh_file);
2736 			return;
2737 		}
2738 		if (doc.Parse(buf, size) != XML_SUCCESS)
2739 		{
2740 			errorLog("Failed to load save data: " + teh_file + " -- Error: " + doc.ErrorStr());
2741 			return;
2742 		}
2743 	}
2744 
2745 	teh_file = dsq->continuity.getSaveFileName(slot, "xml");
2746 	if (exists(teh_file))
2747 	{
2748 		if (readXML(teh_file, doc) != XML_SUCCESS)
2749 			errorLog("Failed to load save data: " + teh_file);
2750 	}
2751 }
2752 
loadFile(int slot)2753 void Continuity::loadFile(int slot)
2754 {
2755 	dsq->user.save();
2756 
2757 	XMLDocument doc;
2758 	loadFileData(slot, doc);
2759 
2760 	XMLElement *startData = doc.FirstChildElement("StartData");
2761 	if (startData)
2762 	{
2763 		if (startData->Attribute("mod"))
2764 		{
2765 #ifdef AQUARIA_DEMO
2766 			exit_error("The demo version does not support loading savegames from mods, sorry.");
2767 #else
2768 			dsq->mod.load(startData->Attribute("mod"));
2769 #endif
2770 		}
2771 	}
2772 
2773 	this->reset();
2774 
2775 	int versionMajor=-1, versionMinor=-1, versionRevision=-1;
2776 	XMLElement *xmlVersion = doc.FirstChildElement("Version");
2777 	if (xmlVersion)
2778 	{
2779 		versionMajor = atoi(xmlVersion->Attribute("major"));
2780 		versionMinor = atoi(xmlVersion->Attribute("minor"));
2781 		versionRevision = atoi(xmlVersion->Attribute("revision"));
2782 	}
2783 
2784 	XMLElement *e = doc.FirstChildElement("Flag");
2785 	while (e)
2786 	{
2787 		dsq->continuity.setFlag(e->Attribute("name"), atoi(e->Attribute("value")));
2788 		e = e->NextSiblingElement("Flag");
2789 	}
2790 
2791 	/*
2792 	if (debugEntityflags)
2793 	{
2794 		for (EntityFlags::iterator i = entityFlags.begin(); i != entityFlags.end(); i++)
2795 		{
2796 			if ((*i).first == name)
2797 			{
2798 				std::ostringstream os;
2799 				os << "Duplicate entity flag: " << name << " please report this error";
2800 				errorLog(os.str());
2801 			}
2802 		}
2803 	}
2804 	*/
2805 
2806 	XMLElement *efx = doc.FirstChildElement("EFX");
2807 	if (efx)
2808 	{
2809 		if (efx->Attribute("a"))
2810 		{
2811 			std::istringstream is(efx->Attribute("a"));
2812 			std::string name;
2813 			while (is >> name)
2814 			{
2815 				is >> entityFlags[name];
2816 			}
2817 		}
2818 	}
2819 
2820 	XMLElement *eats = doc.FirstChildElement("eats");
2821 	if (eats)
2822 	{
2823 		if (eats->Attribute("a"))
2824 		{
2825 			std::istringstream is(eats->Attribute("a"));
2826 			int num = 0;
2827 			naijaEats.clear();
2828 			is >> num;
2829 			for (int i = 0; i < num; i++)
2830 			{
2831 				EatData eat;
2832 				is >> eat.name >> eat.shot >> eat.ammo >> eat.ammoUnitSize;
2833 				naijaEats.push_back(eat);
2834 			}
2835 		}
2836 	}
2837 
2838 	XMLElement *bcn = doc.FirstChildElement("bcn");
2839 	if (bcn)
2840 	{
2841 		if (bcn->Attribute("a"))
2842 		{
2843 			beacons.clear();
2844 
2845 			std::istringstream is(bcn->Attribute("a"));
2846 			int idx=0;
2847 			while (is >> idx)
2848 			{
2849 				BeaconData data;
2850 
2851 				data.index = idx;
2852 				is >> data.on;
2853 				is >> data.color.x >> data.color.y >> data.color.z;
2854 				is >> data.pos.x >> data.pos.y >> data.pos.z;
2855 
2856 				beacons.push_back(data);
2857 			}
2858 		}
2859 	}
2860 
2861 	XMLElement *vox = doc.FirstChildElement("VO");
2862 	if (vox)
2863 	{
2864 		std::string s = vox->Attribute("v");
2865 		std::istringstream is(s);
2866 		std::string v;
2867 		while (is >> v)
2868 		{
2869 			dsq->continuity.voiceOversPlayed.push_back(v);
2870 		}
2871 	}
2872 
2873 	XMLElement *gems = doc.FirstChildElement("Gems");
2874 	if (gems)
2875 	{
2876 		if (gems->Attribute("a"))
2877 		{
2878 			std::string s = gems->Attribute("a");
2879 			std::istringstream is(s);
2880 			GemData g;
2881 			while (is >> g.name)
2882 			{
2883 				is >> g.pos.x >> g.pos.y;
2884 				this->gems.push_back(g);
2885 			}
2886 		}
2887 		else if (gems->Attribute("b"))
2888 		{
2889 			std::string s = gems->Attribute("b");
2890 			std::istringstream is(s);
2891 			GemData g;
2892 			bool hasUserString = false;
2893 			while (is >> g.name)
2894 			{
2895 				hasUserString=false;
2896 
2897 				is >> g.pos.x >> g.pos.y;
2898 				is >> g.canMove;
2899 				is >> hasUserString;
2900 
2901 				if (hasUserString)
2902 					is >> g.userString;
2903 
2904 				std::ostringstream os;
2905 				os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
2906 				debugLog(os.str());
2907 
2908 				g.userString = underscoresToSpaces(g.userString);
2909 				this->gems.push_back(g);
2910 			}
2911 		}
2912 		// num [name mapX mapY canMove hasUserString (userString)]
2913 		else if (gems->Attribute("c"))
2914 		{
2915 			std::string s = gems->Attribute("c");
2916 			std::istringstream is(s);
2917 
2918 			int num = 0;
2919 			is >> num;
2920 
2921 			bool hasUserString = false;
2922 			GemData g;
2923 
2924 			std::ostringstream os;
2925 			os << "continuity num: [" << num << "]" << std::endl;
2926 			os << "data: [" << s << "]" << std::endl;
2927 			debugLog(os.str());
2928 
2929 			for (int i = 0; i < num; i++)
2930 			{
2931 				g.pos = Vector(0,0,0);
2932 				g.canMove = false;
2933 				g.userString = "";
2934 
2935 				hasUserString=false;
2936 
2937 				is >> g.name;
2938 				is >> g.pos.x >> g.pos.y;
2939 				is >> g.canMove;
2940 				is >> hasUserString;
2941 
2942 				if (hasUserString)
2943 					is >> g.userString;
2944 				else
2945 					g.userString = "";
2946 
2947 				g.userString = underscoresToSpaces(g.userString);
2948 				this->gems.push_back(g);
2949 
2950 				std::ostringstream os;
2951 				os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
2952 				debugLog(os.str());
2953 			}
2954 		}
2955 		// num [name hasMapName (mapName) mapX mapY canMove hasUserString (userString)]
2956 		else if (gems->Attribute("d"))
2957 		{
2958 			std::string s = gems->Attribute("d");
2959 			std::istringstream is(s);
2960 
2961 			int num = 0;
2962 			is >> num;
2963 
2964 			bool hasUserString = false;
2965 			bool hasMapName = false;
2966 			GemData g;
2967 
2968 			std::ostringstream os;
2969 			os << "continuity num: [" << num << "]" << std::endl;
2970 			os << "data: [" << s << "]" << std::endl;
2971 			debugLog(os.str());
2972 
2973 			for (int i = 0; i < num; i++)
2974 			{
2975 				g.pos = Vector(0,0,0);
2976 				g.canMove = false;
2977 				g.userString = "";
2978 				g.mapName = "";
2979 
2980 				hasUserString=false;
2981 				hasMapName = false;
2982 
2983 				is >> g.name;
2984 				is >> hasMapName;
2985 				if(hasMapName)
2986 					is >> g.mapName;
2987 
2988 				is >> g.pos.x >> g.pos.y;
2989 				is >> g.canMove;
2990 				is >> hasUserString;
2991 
2992 				if (hasUserString)
2993 					is >> g.userString;
2994 
2995 				g.userString = underscoresToSpaces(g.userString);
2996 				this->gems.push_back(g);
2997 
2998 				std::ostringstream os;
2999 				os << "Loading a Gem called [" << g.name << "] with userString [" << g.userString << "] pos (" << g.pos.x << ", " << g.pos.y << ")\n";
3000 				debugLog(os.str());
3001 			}
3002 		}
3003 	}
3004 
3005 	XMLElement *worldMap = doc.FirstChildElement("WorldMap");
3006 	if (worldMap)
3007 	{
3008 		if (worldMap->Attribute("b"))
3009 		{
3010 			std::string s = worldMap->Attribute("b");
3011 			std::istringstream is(s);
3012 			int idx;
3013 			while (is >> idx)
3014 			{
3015 				dsq->continuity.worldMap.revealMapIndex(idx);
3016 			}
3017 		}
3018 
3019 
3020 #ifdef AQUARIA_BUILD_MAPVIS
3021 		if (worldMap->Attribute("va") && dsq->continuity.worldMap.getNumWorldMapTiles())
3022 		{
3023 			std::istringstream is(worldMap->Attribute("va"));
3024 
3025 			WorldMapTile dummy;
3026 
3027 			int idx;
3028 
3029 			//worldMapTiles.clear();
3030 
3031 			while (is >> idx)
3032 			{
3033 				WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(idx);
3034 
3035 				if (!tile)
3036 				{
3037 					std::ostringstream os;
3038 					os << "tile dummy: dropping data for worldmap tile index " << idx;
3039 					debugLog(os.str());
3040 					tile = &dummy;
3041 				}
3042 
3043 				tile->stringToData(is);
3044 			}
3045 		}
3046 #endif
3047 	}
3048 
3049 
3050 	XMLElement *s = doc.FirstChildElement("Story");
3051 	if (s)
3052 	{
3053 		std::istringstream is(s->Attribute("v"));
3054 		is >> story;
3055 	}
3056 
3057 	XMLElement *e2 = doc.FirstChildElement("StringFlag");
3058 	while (e2)
3059 	{
3060 		dsq->continuity.setStringFlag(e2->Attribute("name"), e2->Attribute("value"));
3061 		e2 = e2->NextSiblingElement("StringFlag");
3062 	}
3063 
3064 	if (startData)
3065 	{
3066 		int x = atoi(startData->Attribute("x"));
3067 		int y = atoi(startData->Attribute("y"));
3068 		dsq->game->positionToAvatar = Vector(x,y);
3069 		if (startData->Attribute("exp"))
3070 			exp = atoi(startData->Attribute("exp"));
3071 
3072 		if (startData->Attribute("naijaModel"))
3073 		{
3074 			//dsq->continuity.naijaModel = startData->Attribute("naijaModel");
3075 		}
3076 
3077 		if (startData->Attribute("form"))
3078 		{
3079 			dsq->continuity.form = FormType(atoi(startData->Attribute("form")));
3080 		}
3081 
3082 		if (startData->Attribute("ingrNames"))
3083 		{
3084 			std::istringstream is(startData->Attribute("ingrNames"));
3085 			std::string name;
3086 			while (is >> name)
3087 			{
3088 				int amount=0;
3089 				is >> amount;
3090 				IngredientData *data = getIngredientDataByName(name);
3091 				if (data)
3092 				{
3093 					data->amount = 0;
3094 					pickupIngredient(data, amount, false);
3095 				}
3096 			}
3097 		}
3098 		else if (startData->Attribute("ingr")) // use this only if ingrNames does not exist.
3099 		{
3100 			std::istringstream is(startData->Attribute("ingr"));
3101 			int idx;
3102 			while (is >> idx)
3103 			{
3104 				int amount=0;
3105 				is >> amount;
3106 				IngredientData *data = getIngredientDataByIndex(idx);
3107 				if (data)
3108 				{
3109 					data->amount = 0;
3110 					pickupIngredient(data, amount, false);
3111 				}
3112 			}
3113 		}
3114 
3115 		if (startData->Attribute("rec"))
3116 		{
3117 			std::istringstream is(startData->Attribute("rec"));
3118 
3119 			for (int i = 0; i < recipes.size(); i++)
3120 			{
3121 				bool known = false;
3122 				is >> known;
3123 				if (known)
3124 					recipes[i].learn();
3125 			}
3126 		}
3127 
3128 		if (startData->Attribute("songs"))
3129 		{
3130 			std::istringstream is(std::string(startData->Attribute("songs")));
3131 			int v=0;
3132 			while (is >> v)
3133 			{
3134 				knowsSong[v] = true;
3135 			}
3136 		}
3137 
3138 		if (startData->Attribute("formUpgrades"))
3139 		{
3140 			std::istringstream is(std::string(startData->Attribute("formUpgrades")));
3141 			int v = 0;
3142 			while (is >> v)
3143 			{
3144 				learnFormUpgrade(FormUpgradeType(v));
3145 			}
3146 		}
3147 
3148 		if (startData->Attribute("intFlags"))
3149 		{
3150 			std::istringstream is(std::string(startData->Attribute("intFlags")));
3151 			int numFlags;
3152 			is >> numFlags;
3153 			if (numFlags > MAX_FLAGS)
3154 				numFlags = MAX_FLAGS;
3155 			for (int i = 0; i < numFlags; i++)
3156 			{
3157 				is >> intFlags[i];
3158 			}
3159 		}
3160 
3161 		if (startData->Attribute("h"))
3162 		{
3163 			float read = strtof(startData->Attribute("h"), NULL);
3164 			maxHealth = read;
3165 			health = read;
3166 			std::ostringstream os;
3167 			os << "MaxHealth read as: " << maxHealth;
3168 			debugLog(os.str());
3169 
3170 			if (dsq->game->avatar)
3171 			{
3172 				dsq->game->avatar->maxHealth = maxHealth;
3173 				dsq->game->avatar->health = maxHealth;
3174 			}
3175 		}
3176 
3177 		if (startData->Attribute("ch"))
3178 		{
3179 			float h = strtof(startData->Attribute("ch"), NULL);
3180 			health = h;
3181 			std::ostringstream os;
3182 			os << "CurHealth read as: " << health;
3183 			debugLog(os.str());
3184 
3185 			if (dsq->game->avatar)
3186 			{
3187 				dsq->game->avatar->health = h;
3188 			}
3189 		}
3190 
3191 		if (startData->Attribute("seconds"))
3192 		{
3193 			std::istringstream is(startData->Attribute("seconds"));
3194 			is >> dsq->continuity.seconds;
3195 		}
3196 		if (startData->Attribute("costume"))
3197 		{
3198 			dsq->continuity.costume = startData->Attribute("costume");
3199 		}
3200 
3201 		dsq->game->sceneToLoad = startData->Attribute("scene");
3202 
3203 		// Additional data introduced in the android version
3204 
3205 		if(startData->Attribute("blind"))
3206 		{
3207 			float timer = strtof(startData->Attribute("blind"), NULL);
3208 			if(dsq->game->avatar)
3209 				dsq->game->avatar->setBlind(timer);
3210 		}
3211 
3212 		if(startData->Attribute("invincible"))
3213 		{
3214 			float timer = strtof(startData->Attribute("invincible"), NULL);
3215 			setInvincible(timer);
3216 		}
3217 
3218 		if(startData->Attribute("regen"))
3219 		{
3220 			float timer = strtof(startData->Attribute("regen"), NULL);
3221 			setRegen(timer);
3222 		}
3223 
3224 		if(startData->Attribute("trip"))
3225 		{
3226 			float timer = strtof(startData->Attribute("trip"), NULL);
3227 			setTrip(timer);
3228 		}
3229 
3230 		if(startData->Attribute("aura"))
3231 		{
3232 			std::istringstream is(startData->Attribute("aura"));
3233 			int type = AURA_NONE;
3234 			float timer = 0.0f;
3235 			is >> type >> timer;
3236 			auraTimer = timer;
3237 			auraType = (AuraType)type;
3238 			if(dsq->game->avatar)
3239 			{
3240 				dsq->game->avatar->activateAura((AuraType)type);
3241 				dsq->game->avatar->auraTimer = timer;
3242 			}
3243 		}
3244 
3245 		if(startData->Attribute("shieldPoints"))
3246 		{
3247 			float sp = strtof(startData->Attribute("shieldPoints"), NULL);
3248 			if(dsq->game->avatar)
3249 				dsq->game->avatar->shieldPoints = sp;
3250 		}
3251 
3252 #define LOAD_MULTI_SIMPLE(attr, mth) \
3253 		do { if(startData->Attribute(attr)) \
3254 		{ \
3255 			std::istringstream is(startData->Attribute(attr)); \
3256 			float value = 0.0f, timer = 0.0f; \
3257 			is >> value >> timer; \
3258 			this->mth(value, timer); \
3259 		}} while(0)
3260 
3261 		LOAD_MULTI_SIMPLE("biteMult", setBiteMultiplier);
3262 		LOAD_MULTI_SIMPLE("speedMult", setSpeedMultiplier);
3263 		LOAD_MULTI_SIMPLE("defenseMult", setDefenseMultiplier);
3264 		LOAD_MULTI_SIMPLE("energyMult", setEnergy);
3265 		LOAD_MULTI_SIMPLE("petPower", setPetPower);
3266 		LOAD_MULTI_SIMPLE("liPower", setLiPower);
3267 		LOAD_MULTI_SIMPLE("light", setLight);
3268 
3269 #undef LOAD_MULTI_SIMPLE
3270 
3271 		if(startData->Attribute("poison"))
3272 		{
3273 			std::istringstream is(startData->Attribute("poison"));
3274 			float p = 0.0f, pt = 0.0f, pbit = 0.0f;
3275 			is >> p >> pt >> pbit;
3276 			setPoison(p, pt);
3277 			poisonBitTimer.start(pbit);
3278 		}
3279 
3280 		// FIXME: the total web time is seemingly not saved in the file.
3281 		// Not sure if the calculation of the remaining time is correct.
3282 		// Especially because there are two webBitTimer variables in use (in Continuity and Avatar),
3283 		// and both of them access the avatar web. It's thus likely that more points were added than intended. -- FG
3284 		if(startData->Attribute("web"))
3285 		{
3286 			std::istringstream is(startData->Attribute("web"));
3287 			float wbit = 0.0f;
3288 			int nump = 0;
3289 			is >> wbit >> nump;
3290 			// 2 web points are added in setWeb() by default, so we exclude them from the calculation
3291 			float remainTime = webTime - (0.5 * (nump - 2)); // Avatar::webBitTimer ticks every 0.5 secs
3292 			if(nump > 1 && remainTime > 0 && dsq->game->avatar)
3293 			{
3294 				if(!dsq->game->avatar->web)
3295 					dsq->game->avatar->createWeb();
3296 				Web *w = dsq->game->avatar->web;
3297 				for(int i = 0; i < nump; ++i)
3298 				{
3299 					Vector v;
3300 					is >> v.x >> v.y;
3301 					if(i < w->getNumPoints())
3302 						w->setPoint(i, v);
3303 					else
3304 						w->addPoint(v);
3305 				}
3306 				webBitTimer.start(wbit);
3307 				webTimer.start(remainTime);
3308 			}
3309 		}
3310 
3311 		// This is AFAIK not in the android version, but let's add this for completeness
3312 		// and to avoid the mess described above.
3313 		if(startData->Attribute("webTimer"))
3314 		{
3315 			float timer = strtof(startData->Attribute("webTimer"), NULL);
3316 			webTimer.start(timer);
3317 		}
3318 
3319 	}
3320 }
3321 
setNaijaModel(std::string model)3322 void Continuity::setNaijaModel(std::string model)
3323 {
3324 	/*
3325 	naijaModel = model;
3326 	if (dsq->game->avatar)
3327 	{
3328 		dsq->game->avatar->refreshModel();
3329 	}
3330 	*/
3331 }
3332 
getFlag(int flag)3333 int Continuity::getFlag(int flag)
3334 {
3335 	if (flag == 0)
3336 	{
3337 		errorLog("Flag 0 not allowed");
3338 	}
3339 
3340 	return intFlags[flag];
3341 }
3342 
setFlag(int flag,int v)3343 void Continuity::setFlag(int flag, int v)
3344 {
3345 	if (flag == 0)
3346 	{
3347 		errorLog("Flag 0 not allowed");
3348 	}
3349 	std::ostringstream os;
3350 	os << "setting flag [" << flag << "] to " << v;
3351 	debugLog(os.str());
3352 	intFlags[flag] = v;
3353 }
3354 
getEntityFlag(const std::string & sceneName,int id)3355 int Continuity::getEntityFlag(const std::string &sceneName, int id)
3356 {
3357 	std::ostringstream os;
3358 	os << sceneName << ":" << id;
3359 
3360 	std::ostringstream os2;
3361 	os2 << hash(os.str());
3362 
3363 	return entityFlags[os2.str()];
3364 }
3365 
setEntityFlag(const std::string & sceneName,int id,int v)3366 void Continuity::setEntityFlag(const std::string &sceneName, int id, int v)
3367 {
3368 	std::ostringstream os;
3369 	os << sceneName << ":" << id;
3370 
3371 	std::ostringstream os2;
3372 	os2 << hash(os.str());
3373 
3374 	entityFlags[os2.str()] = v;
3375 }
3376 
setPathFlag(Path * p,int v)3377 void Continuity::setPathFlag(Path *p, int v)
3378 {
3379 	std::ostringstream os;
3380 	os << "p:" << dsq->game->sceneName << ":" << p->nodes[0].position.x << ":" << p->nodes[0].position.y << ":" << removeSpaces(p->name);
3381 
3382 	std::ostringstream os2;
3383 	os2 << hash(os.str());
3384 
3385 	entityFlags[os2.str()] = v;
3386 }
3387 
getPathFlag(Path * p)3388 int Continuity::getPathFlag(Path *p)
3389 {
3390 	std::ostringstream os;
3391 	os << "p:" << dsq->game->sceneName << ":" << p->nodes[0].position.x << ":" << p->nodes[0].position.y << ":" << removeSpaces(p->name);
3392 	std::ostringstream os2;
3393 	os2 << hash(os.str());
3394 	return entityFlags[os2.str()];
3395 }
3396 
3397 class GemGet : public Quad
3398 {
3399 public:
GemGet(const std::string & gem)3400 	GemGet(const std::string &gem)
3401 	{
3402 		timeScale = 3;
3403 
3404 		timer = 0;
3405 
3406 		//GemGet *q = this;
3407 
3408 		setTexture("Gems/" + gem);
3409 
3410 		followCamera = 1;
3411 
3412 		scale = Vector(0, 0);
3413 		scale.ensureData();
3414 		scale.data->path.addPathNode(Vector(0,0), 0);
3415 		scale.data->path.addPathNode(Vector(1,1), 0.3);
3416 		scale.data->path.addPathNode(Vector(1,1), 0.6);
3417 		scale.data->path.addPathNode(Vector(0.5,0.5), 0.9);
3418 		scale.data->path.addPathNode(Vector(0.1,0.1), 1);
3419 		scale.startPath(timeScale);
3420 
3421 		position = Vector(400,400);
3422 
3423 		setLife(timeScale+0.1f);
3424 		setDecayRate(1);
3425 
3426 		/*
3427 		q->position.path.addPathNode(Vector(400,400), 0.6);
3428 		q->position.path.addPathNode(dsq->game->miniMapRender->position, 0.9);
3429 		q->position.startPath(t);
3430 		*/
3431 	}
3432 protected:
3433 	float timer;
3434 	float timeScale;
3435 	Vector startPos;
onUpdate(float dt)3436 	void onUpdate(float dt)
3437 	{
3438 		Quad::onUpdate(dt);
3439 
3440 		timer += dt;
3441 		if (timer > 0.6f*timeScale)
3442 		{
3443 			if (startPos.isZero())
3444 			{
3445 				startPos = position;
3446 			}
3447 			else
3448 			{
3449 				float p = (timer - (0.6f*timeScale)) / (0.4f*timeScale);
3450 				position = ((dsq->game->miniMapRender->position + dsq->game->miniMapRender->offset) - startPos)*p + startPos;
3451 			}
3452 		}
3453 	}
3454 };
3455 
3456 // WARNING: invalidates pointers to gems!
3457 // BUT: maybe not
removeGemData(GemData * gemData)3458 void Continuity::removeGemData(GemData *gemData)
3459 {
3460 	for (Gems::iterator i = gems.begin(); i != gems.end(); i++)
3461 	{
3462 		if (&(*i) == gemData)
3463 		{
3464 			gems.erase(i);
3465 			return; // safety first
3466 		}
3467 	}
3468 }
3469 
pickupGem(std::string name,bool effects)3470 GemData *Continuity::pickupGem(std::string name, bool effects)
3471 {
3472 	GemData g;
3473 	g.name = name;
3474 	g.mapName = dsq->game->sceneName;
3475 	int sz = gems.size();
3476 
3477 	//HACK: (hacky) using effects to determine the starting position of the gem
3478 	if (!effects)
3479 	{
3480 		g.pos = dsq->game->worldMapRender->getAvatarWorldMapPosition() + Vector(sz*16-64, -64);
3481 	}
3482 	else
3483 	{
3484 		if (!gems.empty())
3485 			g.pos = dsq->game->worldMapRender->getAvatarWorldMapPosition();
3486 		else
3487 			g.pos = Vector(0,0);
3488 	}
3489 
3490 	gems.push_back(g);
3491 
3492 	if (effects && dsq->game->isActive())
3493 	{
3494 		core->sound->playSfx("Gem-Collect");
3495 
3496 		GemGet *gg = new GemGet(g.name);
3497 		dsq->game->addRenderObject(gg, LR_MINIMAP);
3498 		/*
3499 		Quad *q = new Quad;
3500 		q->setTexture("Gems/" + g.name);
3501 
3502 		q->followCamera = 1;
3503 		q->scale = Vector(0, 0);
3504 		q->scale.path.addPathNode(Vector(0,0), 0);
3505 		q->scale.path.addPathNode(Vector(1,1), 0.3);
3506 		q->scale.path.addPathNode(Vector(1,1), 0.6);
3507 		q->scale.path.addPathNode(Vector(0.5,0.5), 0.9);
3508 		q->scale.path.addPathNode(Vector(0.1,0.1), 1);
3509 		q->scale.startPath(t);
3510 
3511 		q->position = Vector(400,400);
3512 		q->position.path.addPathNode(Vector(400,400), 0.6);
3513 		q->position.path.addPathNode(dsq->game->miniMapRender->position, 0.9);
3514 		q->position.startPath(t);
3515 
3516 		dsq->game->addRenderObject(q, LR_MESSAGEBOX);
3517 		*/
3518 
3519 
3520 		if (!getFlag("tokenHint"))
3521 		{
3522 			setFlag("tokenHint", 1);
3523 			dsq->game->setControlHint(dsq->continuity.stringBank.get(4), false, false, false, 8);
3524 		}
3525 		//dsq->watch(1);
3526 		//dsq->resetTimer();
3527 	}
3528 
3529 	// return the last one
3530 	return &gems.back();
3531 }
3532 
entityDied(Entity * eDead)3533 void Continuity::entityDied(Entity *eDead)
3534 {
3535 	if (statsAndAchievements)
3536 	{
3537 		statsAndAchievements->entityDied(eDead);
3538 	}
3539 }
3540 
learnRecipe(Recipe * r,bool effects)3541 void Continuity::learnRecipe(Recipe *r, bool effects)
3542 {
3543 	bool k = r->isKnown();
3544 	r->learn();
3545 
3546 	std::ostringstream os;
3547 	os << "learned recipe: " << r->result << " @ idx: " << r->index;
3548 	debugLog(os.str());
3549 
3550 	if (!k)
3551 		dsq->game->learnedRecipe(r, effects);
3552 }
3553 
learnRecipe(const std::string & result,bool effects)3554 void Continuity::learnRecipe(const std::string &result, bool effects)
3555 {
3556 	for (int i = 0; i < recipes.size(); i++)
3557 	{
3558 		if (nocasecmp(recipes[i].result, result)==0)
3559 		{
3560 			learnRecipe(&recipes[i], effects);
3561 			return;
3562 		}
3563 	}
3564 }
3565 
reset()3566 void Continuity::reset()
3567 {
3568 	dualFormMode = DUALFORM_LI;
3569 	dualFormCharge = 0;
3570 
3571 	lastMenuPage = MENUPAGE_NONE;
3572 	lastOptionsMenuPage = MENUPAGE_NONE;
3573 
3574 	if (dsq->game)
3575 	{
3576 		dsq->game->currentMenuPage = MENUPAGE_NONE;
3577 		dsq->game->currentFoodPage = 0;
3578 		dsq->game->currentTreasurePage = 0;
3579 		dsq->game->recipeMenu.currentPage = 0;
3580 	}
3581 
3582 	//worldMapTiles.clear();
3583 
3584 	speedMult = biteMult = fishPoison = defenseMult = 1;
3585 	speedMult2 = 1;
3586 	poison = 0;
3587 	energyMult = 0;
3588 	light = petPower = 0;
3589 	liPower = 0;
3590 
3591 	speedMultTimer.stop();
3592 	biteMultTimer.stop();
3593 	fishPoisonTimer.stop();
3594 	defenseMultTimer.stop();
3595 	invincibleTimer.stop();
3596 	regenTimer.stop();
3597 	tripTimer.stop();
3598 	energyTimer.stop();
3599 	poisonTimer.stop();
3600 	poisonBitTimer.stop();
3601 	webTimer.stop();
3602 	webBitTimer.stop();
3603 	lightTimer.stop();
3604 	petPowerTimer.stop();
3605 
3606 	loadTreasureData();
3607 
3608 	stringBank.load();
3609 
3610 	gems.clear();
3611 	beacons.clear();
3612 
3613 	worldMap.load();
3614 
3615 	naijaEats.clear();
3616 	foodSortType = 0;
3617 	ingredients.clear();
3618 
3619 	loadIngredientData(); // must be after clearing ingredients
3620 
3621 	loadPetData();
3622 
3623 	formUpgrades.clear();
3624 
3625 	auraType = AURA_NONE;
3626 	for (int i = 0; i < MAX_FLAGS; i++)
3627 	{
3628 		intFlags[i] = 0;
3629 	}
3630 	voiceOversPlayed.clear();
3631 
3632 	entityFlags.clear();
3633 	knowsSong.clear();
3634 	loadSongBank();
3635 	loadEatBank();
3636 	dsq->loadElementEffects();
3637 	form = FORM_NORMAL;
3638 	//cm.reset();
3639 	naijaModel = "Naija";
3640 	costume = "";
3641 	dsq->emote.load("data/naijaemote.txt");
3642 
3643 	//learnSong(SONG_SONGDOOR1);
3644 
3645 	worldType = WT_NORMAL;
3646 
3647 	zoom = Vector(1,1,0);
3648 	itemSlots.clear();
3649 
3650 	seconds = 0;
3651 	exp = 0;
3652 	hudVisible = true;
3653 
3654 	flags.clear();
3655 
3656 	stringFlags.clear();
3657 	story = 0;
3658 
3659 	maxHealth = 5;
3660 	health = maxHealth;
3661 
3662 	speedTypes.clear();
3663 	InStream inFile("data/speedtypes.txt");
3664 	int n;
3665 	float spd;
3666 	while (inFile >> n)
3667 	{
3668 		inFile >> spd;
3669 		speedTypes.push_back(spd);
3670 	}
3671 	//selectedSpell = SpellType(0);
3672 
3673 	if (!dsq->mod.isActive())
3674 	{
3675 		learnSong(SONG_SHIELDAURA);
3676 	}
3677 
3678 	initFoodSort();
3679 
3680 	core->resetTimer();
3681 }
3682 
getSpeedType(int speedType)3683 float Continuity::getSpeedType(int speedType)
3684 {
3685 	if (speedType >= speedTypes.size() || speedType < 0)
3686 	{
3687 		std::ostringstream os;
3688 		os << "speedType: " << speedType << " out of range";
3689 		debugLog(os.str());
3690 		return 0;
3691 	}
3692 	return speedTypes[speedType];
3693 }
3694 
achieve(const std::string & achievement)3695 void Continuity::achieve(const std::string &achievement)
3696 {
3697 }
3698 
flingMonkey(Entity * e)3699 void Continuity::flingMonkey(Entity *e)
3700 {
3701 	statsAndAchievements->flingMonkey(e);
3702 }
3703