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