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 "../BBGE/AfterEffect.h"
22 #include "../BBGE/MathFunctions.h"
23 
24 #include "Avatar.h"
25 #include "Game.h"
26 #include "Shot.h"
27 #include "GridRender.h"
28 
29 //#include "CommonEvents.h"
30 
31 //#include <float.h>
32 
33 //#define AQ_TEST_QUADTRAIL
34 
35 #ifdef AQ_TEST_QUADTRAIL
36 	#include "QuadTrail.h"
37 
38 	QuadTrail *quadTrail = 0;
39 #endif
40 
41 Path *lastWaterBubble = 0;
42 bool lastJumpOutFromWaterBubble = false;
43 
44 bool useSpiritDistance = true;
45 bool inSpiritWorld = false;
46 
47 const float MULT_DMG_CRABCOSTUME = 0.75;
48 const float MULT_DMG_FISHFORM = 1.5;
49 const float MULT_DMG_SEAHORSEARMOR = 0.6;
50 
51 const float MULT_MAXSPEED_BEASTFORM = 1.2;
52 const float MULT_MAXSPEED_FISHFORM = 1.5;
53 
54 const float MULT_DMG_EASY	= 0.5;
55 
56 const float JELLYCOSTUME_HEALTHPERC		= 0.5;
57 const float JELLYCOSTUME_HEALDELAY		= 2.0;
58 const float	JELLYCOSTUME_HEALAMOUNT		= 0.5;
59 
60 const float biteTimerBiteRange = 0.6;
61 const float biteTimerMax = 3;
62 const float biteDelayPeriod = 0.08;
63 const int normalTendrilHits = 3;
64 const int rollTendrilHits = 4;
65 const int maxTendrilHits = 6;
66 
67 const float fireDelayTime = 0.2;
68 const int maxShieldPoints = 8;
69 const int minMouse = 60;
70 int SongIcon::notesOpen = 0;
71 Avatar *avatar = 0;
72 const Vector BLIND_COLOR = Vector(0.1, 0.1, 0.1);
73 const float ANIM_TRANSITION	= 0.2;
74 const float MANA_RECHARGE_RATE = 1.0;
75 const int AURA_SHIELD_RADIUS = 64;
76 //const int TARGET_RANGE = 1024;
77 const int TARGET_RANGE = 1024; // 650
78 const int TARGET_GRACE_RANGE = 200;
79 //const int TARGET_RANGE = 700;
80 //const int TARGET_RANGE = 64;
81 const float NOTE_SCALE = 0.75;
82 const int singingInterfaceRadius = 100;
83 const int openSingingInterfaceRadius = 128;
84 //164
85 const int BURST_DISTANCE = 200;
86 const int STOP_DISTANCE = 48;
87 const int maxMouse = BURST_DISTANCE;
88 //const int SHOCK_RANGE	= 700;
89 const int SHOCK_RANGE	= 1000;
90 const int SPIRIT_RANGE	= 2000;
91 
92 const float QUICK_SONG_CAST_DELAY = 0.4;
93 
94 const float BURST_RECOVER_RATE = 1.2; // 3.0 // 0.75
95 const float BURST_USE_RATE = 1.5; //0.9 //1.5;
96 const float BURST_ACCEL = 4000; //2000 // 1000
97 
98 // Minimum time between two splash effects (seconds).
99 const float SPLASH_INTERVAL = 0.2;
100 
101 const float TUMMY_TIME = 6.0;
102 
103 const float chargeMax = 2.0;
104 
105 // Axis input distance (0.0-1.0) at which we start moving.
106 const float JOYSTICK_LOW_THRESHOLD = 0.2;
107 // Axis input distance at which we move full speed.
108 const float JOYSTICK_HIGH_THRESHOLD = 0.6;
109 // Axis input distance at which we accept a note.
110 const float JOYSTICK_NOTE_THRESHOLD = 0.6;
111 
112 // Mouse cursor distance (from note icon, in virtual pixels) below which
113 // we accept a note.
114 const float NOTE_ACCEPT_DISTANCE = 25;
115 // Joystick input angle offset (from note icon, in degrees) below which
116 // we accept a note.
117 const float NOTE_ACCEPT_ANGLE_OFFSET = 15;
118 
119 const int COLLIDE_RADIUS_NORMAL = 10;
120 const int COLLIDE_RADIUS_FISH = 8;
121 
122 const int COLLIDE_RANGE_NORMAL = 2;
123 const int COLLIDE_RANGE_FISH = 1;
124 
125 const float COLLIDE_MOD_NORMAL = 1.0f;
126 const float COLLIDE_MOD_FISH = 0.1f;
127 
128 const int requiredDualFormCharge = 3;
129 
130 bool usingDigital = false;
131 
132 Bone *bone_head = 0;
133 Bone *bone_dualFormGlow = 0;
134 
135 
136 bool _isUnderWater;
137 
138 //HRECORD avatarRecord = 0;
139 
140 
getWorldPosition()141 Vector Target::getWorldPosition()
142 {
143 	Vector ret;
144 	if (e)
145 	{
146 		ret = e->getTargetPoint(targetPt);
147 	}
148 	return ret;
149 }
150 
bindInput()151 void Avatar::bindInput()
152 {
153 	ActionMapper::clearActions();
154 	ActionMapper::clearCreatedEvents();
155 
156 
157 	dsq->user.control.actionSet.importAction(this, "PrimaryAction", ACTION_PRIMARY);
158 	dsq->user.control.actionSet.importAction(this, "SecondaryAction", ACTION_SECONDARY);
159 
160 	dsq->user.control.actionSet.importAction(this, "Revert",		ACTION_REVERT);
161 
162 	dsq->user.control.actionSet.importAction(this, "SwimUp",		ACTION_SWIMUP);
163 	dsq->user.control.actionSet.importAction(this, "SwimDown",		ACTION_SWIMDOWN);
164 	dsq->user.control.actionSet.importAction(this, "SwimLeft",		ACTION_SWIMLEFT);
165 	dsq->user.control.actionSet.importAction(this, "SwimRight",		ACTION_SWIMRIGHT);
166 
167 	/*
168 	dsq->user.control.actionSet.importAction(this, "SingUp",		ACTION_SINGUP);
169 	dsq->user.control.actionSet.importAction(this, "SingDown",		ACTION_SINGDOWN);
170 	dsq->user.control.actionSet.importAction(this, "SingLeft",		ACTION_SINGLEFT);
171 	dsq->user.control.actionSet.importAction(this, "SingRight",		ACTION_SINGRIGHT);
172 	*/
173 
174 	dsq->user.control.actionSet.importAction(this, "SongSlot1",		ACTION_SONGSLOT1);
175 	dsq->user.control.actionSet.importAction(this, "SongSlot2",		ACTION_SONGSLOT2);
176 	dsq->user.control.actionSet.importAction(this, "SongSlot3",		ACTION_SONGSLOT3);
177 	dsq->user.control.actionSet.importAction(this, "SongSlot4",		ACTION_SONGSLOT4);
178 	dsq->user.control.actionSet.importAction(this, "SongSlot5",		ACTION_SONGSLOT5);
179 	dsq->user.control.actionSet.importAction(this, "SongSlot6",		ACTION_SONGSLOT6);
180 	dsq->user.control.actionSet.importAction(this, "SongSlot7",		ACTION_SONGSLOT7);
181 	dsq->user.control.actionSet.importAction(this, "SongSlot8",		ACTION_SONGSLOT8);
182 	dsq->user.control.actionSet.importAction(this, "SongSlot9",		ACTION_SONGSLOT9);
183 	dsq->user.control.actionSet.importAction(this, "SongSlot10",	ACTION_SONGSLOT10);
184 
185 	dsq->user.control.actionSet.importAction(this, "Look",			ACTION_LOOK);
186 
187 	/*
188 	dsq->user.control.actionSet.importAction(this, "SongSlot5", "f5");
189 	dsq->user.control.actionSet.importAction(this, "SongSlot6", "f6");
190 	dsq->user.control.actionSet.importAction(this, "SongSlot7", "f7");
191 	dsq->user.control.actionSet.importAction(this, "SongSlot8", "f8");
192 	*/
193 
194 	dsq->user.control.actionSet.importAction(this, "Roll",			ACTION_ROLL);
195 
196 	/*
197 	// song note keys
198 	addAction("s1", KEY_1);
199 	addAction("s2", KEY_2);
200 	addAction("s3", KEY_3);
201 	addAction("s4", KEY_4);
202 	addAction("s5", KEY_5);
203 	addAction("s6", KEY_6);
204 	addAction("s7", KEY_7);
205 	addAction("s8", KEY_8);
206 	*/
207 
208 }
209 
getNotesOpen()210 int Avatar::getNotesOpen()
211 {
212 	return SongIcon::notesOpen;
213 }
214 
215 // note: z is set to 1.0 when we want the aim to be used as the shot direction
216 // otherwise the shot will head straight to the target
getAim()217 Vector Avatar::getAim()
218 {
219 	Vector d;
220 	if (dsq->inputMode == INPUT_JOYSTICK)
221 	{
222 		if (!core->joystick.rightStick.isZero())
223 		{
224 			d = core->joystick.rightStick * 300;
225 			d.z = 1;
226 		}
227 		else
228 		{
229 			d = core->joystick.position * 300;
230 			d.z = 0;
231 		}
232 	}
233 	else if (dsq->inputMode == INPUT_KEYBOARD)
234 	{
235 		d = dsq->getGameCursorPosition() - position;
236 		d.z = 1;
237 	}
238 	else
239 	{
240 		d = dsq->getGameCursorPosition() - position;
241 		d.z = 1;
242 	}
243 	if (d.isZero())
244 		d = getForwardAim();
245 	return d;
246 }
247 
getForwardAim()248 Vector Avatar::getForwardAim()
249 {
250 	Vector aim = getForward();
251 	// getForward() points toward Naija's head, but it makes more sense
252 	// to shoot in the direction she's facing, so rotate the aim vector.
253 	aim = getRotatedVector(aim, isfh() ? 90 : -90);
254 	return aim;
255 }
256 
postInit()257 void Avatar::postInit()
258 {
259 	// post init isn't early enough
260 	/*
261 	Entity::postInit();
262 	*/
263 }
264 
onAnimationKeyPassed(int key)265 void Avatar::onAnimationKeyPassed(int key)
266 {
267 	Entity::onAnimationKeyPassed(key);
268 }
269 
doBounce()270 void Avatar::doBounce()
271 {
272 	float ba = 0.75;
273 	if (isRolling())
274 		ba = 1.0;
275 	float len = vel.getLength2D();
276 	Vector I = vel/len;
277 	Vector N = dsq->game->getWallNormal(position);
278 
279 	if (!N.isZero())
280 	{
281 		//2*(-I dot N)*N + I
282 		vel = 2*(-I.dot(N))*N + I;
283 		vel.setLength2D(len*ba);
284 	}
285 }
286 
randCirclePos(Vector position,int radius)287 Vector randCirclePos(Vector position, int radius)
288 {
289 	float a = ((rand()%360)*(2*PI))/360.0f;
290 	return position + Vector(sinf(a), cosf(a))*radius;
291 }
292 
SongIconParticle(Vector color,Vector pos,int note)293 SongIconParticle::SongIconParticle(Vector color, Vector pos, int note)
294 								:  note(note)
295 {
296 	cull = false;
297 	//fastTransform = true;
298 	setTexture("particles/glow");
299 
300 	setWidthHeight(32);
301 
302 	float life = 1.0;
303 
304 	toIcon = 0;
305 	this->color = color;
306 	position = pos;
307 
308 	alpha.ensureData();
309 	alpha.data->path.addPathNode(0, 0);
310 	alpha.data->path.addPathNode(0.4, 0.2); // .8
311 	alpha.data->path.addPathNode(0.2, 0.8); // .4
312 	alpha.data->path.addPathNode(0, 1);
313 	alpha.startPath(life);
314 
315 	scale.ensureData();
316 	scale.data->path.addPathNode(Vector(0.5,0.5), 0);
317 	scale.data->path.addPathNode(Vector(1,1), 0.5);
318 	scale.data->path.addPathNode(Vector(0.5,0.5), 1);
319 	scale.startPath(life);
320 
321 	setLife(life);
322 	setDecayRate(1);
323 
324 	//if (rand()%6 <= 2)
325 	setBlendType(RenderObject::BLEND_ADD);
326 
327 	float smallestDist = HUGE_VALF;
328 	SongIcon *closest = 0;
329 	for (int i = 0; i < avatar->songIcons.size(); i++)
330 	{
331 		if (i != note)
332 		{
333 			Vector diff = (position - avatar->songIcons[i]->position);
334 			float dist = diff.getSquaredLength2D();
335 			if (dist < smallestDist)
336 			{
337 				smallestDist = dist;
338 				closest = avatar->songIcons[i];
339 			}
340 		}
341 	}
342 	// find nearest song icon
343 	if (closest)
344 	{
345 		toIcon = closest;
346 	}
347 }
348 
onUpdate(float dt)349 void SongIconParticle::onUpdate(float dt)
350 {
351 	Quad::onUpdate(dt);
352 
353 	if (toIcon)
354 	{
355 		Vector add = (toIcon->position - position);
356 		add.setLength2D(200*dt);
357 		velocity += add;
358 		velocity.capLength2D(50);
359 	}
360 }
361 
SongIcon(int note)362 SongIcon::SongIcon(int note) : Quad(), note(note)
363 {
364 	open = false;
365 	alphaMod = 0.9;
366 	/*
367 	std::ostringstream os;
368 	os << "SongIcon" << note;
369 	setTexture(os.str());
370 	*/
371 	//setTexture("Cursor-Sing");
372 	std::ostringstream os;
373 	os << "Song/NoteSymbol" << note;
374 	os.str();
375 	setTexture(os.str());
376 
377 	scale = Vector(NOTE_SCALE, NOTE_SCALE);
378 	cursorIsIn = false;
379 	delay = 0;
380 	counter = 0;
381 	width = 40;
382 	height = 40;
383 
384 	minTime = 0;
385 	ptimer = 0;
386 	noteColor = dsq->getNoteColor(note);
387 	//color = dsq->getNoteColor(note)*0.75f + Vector(1,1,1)*0.25f;
388 	color = dsq->getNoteColor(note);
389 	len = 0;
390 
391 	channel = BBGE_AUDIO_NOCHANNEL;
392 
393 	rippleTimer = 0;
394 
395 	glow = new Quad;
396 	glow->setTexture("particles/bigglow");
397 	glow->followCamera = 1;
398 	glow->rotation.interpolateTo(Vector(0,0,360), 10, -1);
399 	glow->alpha = 0;
400 	glow->setBlendType(RenderObject::BLEND_ADD);
401 	glow->scale = Vector(0.5, 0.5);
402 	glow->color = dsq->getNoteColor(note);
403 	dsq->game->addRenderObject(glow, LR_PARTICLES2);
404 }
405 
destroy()406 void SongIcon::destroy()
407 {
408 	Quad::destroy();
409 }
410 
spawnParticles(float dt)411 void SongIcon::spawnParticles(float dt)
412 {
413 	float intv = 0.1;
414 	// do stuff!
415 	ptimer += dt;
416 	while (ptimer > intv)
417 	{
418 		ptimer -= intv;
419 		SongIconParticle *s = new SongIconParticle(noteColor, randCirclePos(position, 16), note);
420 		s->followCamera = true;
421 		dsq->game->addRenderObject(s, LR_HUD);
422 	}
423 }
424 
onUpdate(float dt)425 void SongIcon::onUpdate(float dt)
426 {
427 	Quad::onUpdate(dt);
428 
429 	if (!avatar->singing)
430 		return;
431 
432 	if (alpha.x == 0 && !alpha.isInterpolating())
433 		alpha.interpolateTo(0.3, 0.1);
434 	if (delay > 0)
435 	{
436 		delay -= dt;
437 		if (delay < 0)
438 		{
439 			delay = 0;
440 			//channel = BBGE_AUDIO_NOCHANNEL;
441 		}
442 	}
443 	if (counter > 0)
444 	{
445 		counter -= dt;
446 		if (counter < 0)
447 		{
448 			counter = 0;
449 			closeNote();
450 		}
451 	}
452 	if (alpha.x > 0.5f)
453 	{
454 		spawnParticles(dt);
455 	}
456 	if (open)
457 	{
458 		len += dt;
459 		avatar->setHeadTexture("Singing", 0.1);
460 	}
461 	if (alpha.x == 1)
462 	{
463 		if (isCoordinateInRadius(core->mouse.position, NOTE_ACCEPT_DISTANCE))
464 		{
465 			//if (delay == 0)
466 			if (true)
467 			{
468 				if (!cursorIsIn)
469 				// highlighted for the first time
470 				{
471 					cursorIsIn = true;
472 					openNote();
473 				}
474 				else
475 				{
476 					if (minTime > 0)
477 					{
478 						minTime -= dt;
479 						if (minTime < 0)
480 						{
481 							minTime = 0;
482 						}
483 					}
484 
485 				}
486 			}
487 		}
488 		else if (!isCoordinateInRadius(core->mouse.position, NOTE_ACCEPT_DISTANCE*1.25f))
489 		{
490 			if (cursorIsIn)
491 			{
492 				cursorIsIn = false;
493 				closeNote();
494 			}
495 		}
496 	}
497 	if (alpha.x <= 0 && delay == 0) //  && channel != BBGE_AUDIO_NOCHANNEL
498 	{
499 		closeNote();
500 	}
501 
502 	if (open)
503 	{
504 		if (dsq->user.video.noteEffects)
505 		{
506 			rippleTimer -= dt;
507 			if (rippleTimer <= 0)
508 			{
509 				//rippleTimer = 1.0f - ((7 - note)/7.0f)*0.7f;
510 				rippleTimer = 0.5f - (note/7.0f)*0.4f;
511 
512 				if (core->afterEffectManager)
513 				{
514 					core->afterEffectManager->addEffect(new ShockEffect(position - Vector(400, 300) + Vector(core->width/2, core->height/2),
515 					core->screenCenter,0.009f,0.015f,18,0.2f, 0.9f + (note*0.08f) ));
516 				}
517 			}
518 		}
519 	}
520 
521 	if (glow)
522 	{
523 		glow->position = position;
524 	}
525 }
526 
openNote()527 void SongIcon::openNote()
528 {
529 	//if (delay > 0) return;
530 	scale.interpolateTo(Vector(1.2, 1.2), 0.1);
531 
532 	if (dsq->user.video.noteEffects)
533 	{
534 		glow->scale = Vector(0.5,0.5);
535 		glow->scale.interpolateTo(Vector(1.0, 1.0), 2, -1, 1, 1);
536 
537 		glow->alpha.interpolateTo(0.6, 0.2, 0, 0, 1);
538 	}
539 
540 	/*
541 	std::ostringstream os;
542 	os << "Note"
543 	*/
544 
545 	std::string sfx = dsq->game->getNoteName(note);
546 
547 	open = true;
548 
549 	internalOffset = Vector(-5, 0);
550 	internalOffset.interpolateTo(Vector(5, 0), 0.08, -1, 1);
551 
552 	avatar->singNote(this->note);
553 
554 	// this should never get called:
555 	if (channel != BBGE_AUDIO_NOCHANNEL)
556 	{
557 		dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
558 		//dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
559 		channel = BBGE_AUDIO_NOCHANNEL;
560 	}
561 	//dsq->sound->stopSfx(channel);
562 
563 
564 	PlaySfx play;
565 	play.name = sfx;
566 	channel = dsq->sound->playSfx(play);
567 
568 
569 	rippleTimer = 0;
570 
571 	minTime = 0.05;
572 	counter = 3.2;
573 
574 	float glowLife = 0.5;
575 
576 	{
577 	Quad *q = new Quad("particles/glow", position);
578 	q->scale.interpolateTo(Vector(10, 10), glowLife+0.1f);
579 	q->alpha.ensureData();
580 	q->alpha.data->path.addPathNode(0,0);
581 	q->alpha.data->path.addPathNode(0.75,0.2);
582 	q->alpha.data->path.addPathNode(0,1);
583 	q->alpha.startPath(glowLife);
584 	q->color = dsq->getNoteColor(note); //*0.5f + Vector(0.5, 0.5, 0.5)
585 	q->setBlendType(RenderObject::BLEND_ADD);
586 	q->followCamera = 1;
587 	dsq->game->addRenderObject(q, LR_HUD);
588 	q->setDecayRate(1/(glowLife+0.1f));
589 	}
590 
591 	{
592 	std::ostringstream os2;
593 	os2 << "Song/NoteSymbol" << note;
594 
595 	Quad *q = new Quad(os2.str(), position);
596 	q->color = 0;
597 	q->scale = Vector(0.5,0.5);
598 	q->scale.interpolateTo(Vector(2, 2), glowLife+0.1f);
599 	//q->scale.interpolateTo(Vector(10, 10), glowLife+0.1f);
600 	q->alpha.ensureData();
601 	q->alpha.data->path.addPathNode(0,0);
602 	q->alpha.data->path.addPathNode(0.5,0.2);
603 	q->alpha.data->path.addPathNode(0,1);
604 	q->alpha.startPath(glowLife);
605 	//q->setBlendType(RenderObject::BLEND_ADD);
606 	q->followCamera = 1;
607 	dsq->game->addRenderObject(q, LR_HUD);
608 	q->setDecayRate(1/(glowLife+0.1f));
609 	}
610 
611 	avatar->songInterfaceTimer = 1.0;
612 
613 	notesOpen++;
614 	/*
615 	std::ostringstream os2;
616 	os2 << "notesOpen: " << notesOpen;
617 	debugLog(os2.str());
618 	*/
619 	if (notesOpen > 0)
620 	{
621 		len = 0;
622 
623 		FOR_ENTITIES(i)
624 		{
625 			Entity *e = *i;
626 			if ((e->position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
627 			{
628 				e->songNote(note);
629 			}
630 		}
631 		for (int i = 0; i < dsq->game->getNumPaths(); i++)
632 		{
633 			Path *p = dsq->game->getPath(i);
634 			if (!p->nodes.empty())
635 			{
636 				if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
637 				{
638 					p->songNote(note);
639 				}
640 			}
641 		}
642 	}
643 }
644 
closeNote()645 void SongIcon::closeNote()
646 {
647 	//if (delay > 0) return;
648 	scale.interpolateTo(Vector(NOTE_SCALE, NOTE_SCALE), 0.1);
649 
650 	if (dsq->game->avatar->isSinging() && dsq->user.video.noteEffects)
651 		glow->alpha.interpolateTo(0.3, 1.5, 0, 0, 1);
652 	else
653 		glow->alpha.interpolateTo(0, 1.5, 0, 0, 1);
654 	glow->scale.interpolateTo(Vector(0.5, 0.5), 0.5);
655 
656 
657 	cursorIsIn = false;
658 
659 	if (channel != BBGE_AUDIO_NOCHANNEL)
660 	{
661 		dsq->sound->fadeSfx(channel, SFT_OUT, 1.0);
662 		channel = BBGE_AUDIO_NOCHANNEL;
663 		//delay = 0.5;
664 	}
665 
666 	if (open)
667 	{
668 		internalOffset.stop();
669 		internalOffset = Vector(0,0);
670 		notesOpen--;
671 		open = false;
672 
673 		FOR_ENTITIES(i)
674 		{
675 			Entity *e = *i;
676 			int dist = (e->position - dsq->game->avatar->position).getSquaredLength2D();
677 			if (e != dsq->game->avatar && dist < sqr(1000))
678 			{
679 				e->songNoteDone(note, len);
680 			}
681 		}
682 		for (int i = 0; i < dsq->game->getNumPaths(); i++)
683 		{
684 			Path *p = dsq->game->getPath(i);
685 			if (!p->nodes.empty())
686 			{
687 				if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
688 				{
689 					p->songNoteDone(note, len);
690 				}
691 			}
692 		}
693 	}
694 
695 	/*
696 	std::ostringstream os;
697 	os << "notesOpen: " << notesOpen;
698 	debugLog(os.str());
699 	*/
700 
701 	if (notesOpen <= 0)
702 	{
703 		notesOpen = 0;
704 		if (dsq->continuity.form == FORM_NORMAL)
705 			avatar->setHeadTexture("");
706 	}
707 }
708 
openInterface()709 void SongIcon::openInterface()
710 {
711 	delay = 0;
712 	alpha.interpolateTo(1, 0.1);
713 }
714 
closeInterface()715 void SongIcon::closeInterface()
716 {
717 	closeNote();
718 	delay = 0;
719 	alpha.interpolateTo(0, 0.1);
720 }
721 
AvatarState()722 AvatarState::AvatarState()
723 {
724 	abilityDelay = 0;
725 	outOfWaterTimer = 0;
726 	backFlip = false;
727 	nearWall = false;
728 	wasUnderWater = true;
729 	blind = false;
730 	lockedToWall = false;
731 	shotDelay = 0;
732 	spellCharge = 0;
733 	leachTimer = 0;
734 	swimTimer = 0;
735 	rollTimer = 0;
736 	updateLookAtTime = 0;
737 	lookAtEntity = 0;
738 	blinkTimer = 0;
739 }
740 
toggleMovement(bool on)741 void Avatar::toggleMovement(bool on)
742 {
743 	canMove = on;
744 }
745 
isLockable()746 bool Avatar::isLockable()
747 {
748 	return (bursting || !_isUnderWater) && (boneLockDelay == 0) && canLockToWall();
749 }
750 
isSinging()751 bool Avatar::isSinging()
752 {
753 	return singing;
754 }
755 
applyWorldEffects(WorldType type)756 void Avatar::applyWorldEffects(WorldType type)
757 {
758 	static bool oldfh=false;
759 
760 	if (type == WT_SPIRIT)
761 	{
762 		//skeletalSprite.transitionAnimate("ball", 0.1, -1);
763 		//skeletalSprite.alpha.interpolateTo(0, 1);
764 		//skeletalSprite.alpha = 0;
765 		//dsq->game->addRenderObject(&skeletalSprite, LR_ENTITIES);
766 
767 		removeChild(&skeletalSprite);
768 		skeletalSprite.position = position;
769 		skeletalSprite.setFreeze(true);
770 		skeletalSprite.scale = scale;
771 		skeletalSprite.alpha.interpolateTo(0.5, 1);
772 		//dsq->game->addRenderObject(&skeletalSprite, LR_ENTITIES);
773 		skeletalSprite.rotation.z = rotation.z;
774 		skeletalSprite.rotationOffset.z = rotationOffset.z;
775 
776 
777 		oldfh = skeletalSprite.isfh();
778 		skeletalSprite.fhTo(isfh());
779 
780 		renderQuad = true;
781 		setTexture("glow");
782 		width = 256;
783 		height = 256;
784 		setBlendType(BLEND_ADD);
785 		fader->alpha.interpolateTo(0.75, 1);
786 
787 		dsq->sound->toggleEffectMusic(SFX_FLANGE, true);
788 	}
789 	else
790 	{
791 		//skeletalSprite.transitionAnimate("idle", 1, -1);
792 		//skeletalSprite.alpha.interpolateTo(1, 1);
793 		//skeletalSprite.alpha = 1;
794 		//dsq->game->removeRenderObject(&skeletalSprite);
795 
796 		skeletalSprite.setFreeze(false);
797 		if (!skeletalSprite.getParent())
798 		{
799 			addChild(&skeletalSprite, PM_STATIC);
800 		}
801 		skeletalSprite.position = Vector(0,0,0);
802 		skeletalSprite.scale = Vector(1,1,1);
803 		renderQuad = false;
804 		setBlendType(BLEND_DEFAULT);
805 		fader->alpha.interpolateTo(0, 1);
806 
807 		bool newfh = skeletalSprite.isfh();
808 		skeletalSprite.fhTo(oldfh);
809 		skeletalSprite.rotation.z = 0;
810 		skeletalSprite.rotationOffset.z = 0;
811 
812 		fhTo(newfh);
813 
814 		dsq->sound->toggleEffectMusic(SFX_FLANGE, false);
815 	}
816 }
817 
startFlourish()818 void Avatar::startFlourish()
819 {
820 	std::string anim = dsq->continuity.getInternalFormName() + "-flourish";
821 	//if (skeletalSprite.getAnimation(anim))
822 	Animation *fanim = skeletalSprite.getAnimation(anim);
823 	if (fanim)
824 	{
825 		flourishTimer.start(fanim->getAnimationLength()-0.2f);
826 		flourishPowerTimer.start(fanim->getAnimationLength()*0.5f);
827 	}
828 	skeletalSprite.transitionAnimate(anim, 0.1, 0, ANIMLAYER_FLOURISH);
829 	flourish = true;
830 
831 	float rotz = rotationOffset.z;
832 	if (this->isfh())
833 		rotationOffset = Vector(0,0,rotz+360);
834 	else
835 		rotationOffset = Vector(0,0,rotz-360);
836 
837 	FormType f = dsq->continuity.form;
838 	if (f != FORM_NORMAL && f != FORM_BEAST && f != FORM_FISH && f != FORM_SUN && f != FORM_NATURE)
839 	{
840 		rotationOffset.z *= -1;
841 	}
842 	if (f == FORM_ENERGY || f == FORM_DUAL)
843 	{
844 		rotationOffset.z *= 2;
845 	}
846 
847 	if (f == FORM_BEAST)
848 	{
849 		Vector v = getNormal();
850 		if (!v.isZero())
851 		{
852 			v *= 400;
853 			vel += v;
854 		}
855 	}
856 
857 	rotationOffset.interpolateTo(Vector(0,0,rotz), 0.8, 0, 0, 1);
858 }
859 
onIdle()860 void Avatar::onIdle()
861 {
862 	if (dsq->game->li)
863 	{
864 		if (dsq->game->li->getState() == STATE_HUG && riding)
865 		{
866 			dsq->game->li->setState(STATE_IDLE);
867 		}
868 	}
869 	//stillTimer.stop();
870 	stopBurst();
871 	if (movingOn)
872 	{
873 		dsq->setMousePosition(Vector(400,300));
874 	}
875 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
876 	stopRoll();
877 	closeSingingInterface();
878 	fallOffWall();
879 
880 	dsq->gameSpeed.stopPath();
881 	dsq->gameSpeed.interpolateTo(1,0);
882 }
883 
getBurstAnimName()884 std::string Avatar::getBurstAnimName()
885 {
886 	std::string ret;
887 	switch(dsq->continuity.form)
888 	{
889 	case FORM_ENERGY:
890 		ret = "energyburst";
891 	break;
892 	default:
893 		ret = "burst";
894 	break;
895 	}
896 	return ret;
897 }
898 
getRollAnimName()899 std::string Avatar::getRollAnimName()
900 {
901 	std::string ret;
902 	switch(dsq->continuity.form)
903 	{
904 	case FORM_ENERGY:
905 		ret = "energyroll";
906 	break;
907 	default:
908 		ret = "roll";
909 	break;
910 	}
911 	return ret;
912 }
913 
getIdleAnimName()914 std::string Avatar::getIdleAnimName()
915 {
916 	std::string ret="idle";
917 	switch(dsq->continuity.form)
918 	{
919 	case FORM_ENERGY:
920 		ret="energyidle";
921 	break;
922 	}
923 	return ret;
924 }
925 
clampPosition()926 void Avatar::clampPosition()
927 {
928 	lastPosition = position;
929 }
930 
updatePosition()931 void Avatar::updatePosition()
932 {
933 	updateHair(0);
934 }
935 
updateHair(float dt)936 void Avatar::updateHair(float dt)
937 {
938 	static float hairTimer = 0;
939 	Bone *b = skeletalSprite.getBoneByIdx(0);
940 	if (hair && b)
941 	{
942 		hair->alpha.x = alpha.x;
943 		hair->color.x = color.x * multColor.x;
944 		hair->color.y = color.y * multColor.y;
945 		hair->color.z = color.z * multColor.z;
946 		Vector headPos = b->getWorldCollidePosition(Vector(12,-32,0));
947 
948 		hair->setHeadPosition(headPos);
949 		Vector diff = headPos - position;
950 
951 		Vector diff2;
952 		if (!isfh())
953 			diff2 = diff.getPerpendicularLeft();
954 		else
955 			diff2 = diff.getPerpendicularRight();
956 
957 		Vector diff3 = position - headPos;
958 
959 		if (state.lockedToWall && wallPushVec.y < 0 && (fabsf(wallPushVec.y) > fabsf(wallPushVec.x)))
960 		{
961 			if (isfh())
962 			{
963 				diff3 = Vector(-50, -25);
964 			}
965 			else
966 				diff3 = Vector(50,-25);
967 		}
968 
969 		float len =diff2.getLength2D();
970 		diff3.setLength2D(len);
971 		/*
972 		diff.y = -diff.y;
973 		diff = (diff + diff2)/2.0f;
974 		*/
975 
976 		hairTimer += dt;
977 		while (hairTimer > 2.0f)
978 		{
979 			hairTimer -= 2.0f;
980 		}
981 		float useTimer = hairTimer;
982 		if (useTimer > 1.0f)
983 			useTimer = 1.0f - (hairTimer-1);
984 		float frc = 0.333333;
985 		diff = (diff2*(frc*(1.0f-(useTimer*0.5f))) + diff3*(frc) + Vector(0,len)*(frc*(0.5f+useTimer*0.5f)));
986 
987 
988 
989 		if (_isUnderWater)
990 		{
991 			diff.setLength2D(400);
992 			//if (!vel.isLength2DIn(10))
993 			hair->exertForce(diff, dt);
994 		}
995 		else
996 		{
997 			diff.setLength2D(400);
998 			hair->exertForce(diff, dt);
999 		}
1000 		if (!vel2.isZero())
1001 			hair->exertForce(vel2, dt);
1002 		hair->updatePositions();
1003 	}
1004 }
1005 
updateDamageVisualEffects()1006 void Avatar::updateDamageVisualEffects()
1007 {
1008    	int damageThreshold = float(maxHealth/5.0f)*3.0f;
1009 	if (health <= damageThreshold)
1010 	{
1011 		//dsq->game->damageSprite->alpha.interpolateTo(0.9, 0.5);
1012 		float a = ((damageThreshold - health)/float(damageThreshold))*1.0f;
1013 		dsq->game->damageSprite->alpha.interpolateTo(a, 0.3);
1014 
1015 		/*
1016 		std::ostringstream os;
1017 		os << "damageSprite alpha: " << a;
1018 		debugLog(os.str());
1019 		*/
1020 
1021 		dsq->game->damageSprite->scale = Vector(1,1);
1022 		dsq->game->damageSprite->scale.interpolateTo(Vector(1.2, 1.2), 0.5, -1, 1);
1023 
1024 		/*
1025 		if (health <= 0)
1026 		{
1027 			dsq->game->sceneColor.interpolateTo(Vector(1,0.5,0.5), 0.75);
1028 		}
1029 		*/
1030 	}
1031 	else
1032 	{
1033 		dsq->game->damageSprite->alpha.interpolateTo(0, 0.3);
1034 	}
1035 }
1036 
checkUpgradeForShot(Shot * s)1037 void Avatar::checkUpgradeForShot(Shot *s)
1038 {
1039 	if (dsq->continuity.energyMult <= 1)
1040 		s->extraDamage = dsq->continuity.energyMult * 1;
1041 	else
1042 		s->extraDamage = dsq->continuity.energyMult * 0.75f;
1043 
1044 	if (s->extraDamage > 0)
1045 	{
1046 		Quad *glow = new Quad("particles/glow", Vector(0,0));
1047 		glow->color = Vector(1,0,0);
1048 		glow->color.interpolateTo(Vector(1,0.5,0.5), 0.1, -1, 1);
1049 		glow->setBlendType(BLEND_ADD);
1050 		glow->scale = Vector(4, 4) + (s->extraDamage*Vector(2,2));
1051 		glow->scale.interpolateTo(Vector(16,16)+ (s->extraDamage*Vector(2,2)), 0.5, -1, 1);
1052 		s->addChild(glow, PM_POINTER);
1053 	}
1054 }
1055 
onDamage(DamageData & d)1056 void Avatar::onDamage(DamageData &d)
1057 {
1058 	Entity::onDamage(d);
1059 
1060 
1061 	if (dsq->difficulty == DSQ::DIFF_EASY)
1062 	{
1063 		if (d.damage > 0)
1064 			d.damage *= MULT_DMG_EASY;
1065 	}
1066 
1067 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
1068 	if (dsq->continuity.form == FORM_NORMAL)
1069 	{
1070 		if (nocasecmp(dsq->continuity.costume, "CC")==0)
1071 		{
1072 			d.damage *= MULT_DMG_CRABCOSTUME;
1073 		}
1074 	}
1075 
1076 	if (riding != 0)
1077 	{
1078 		if (nocasecmp(dsq->continuity.costume, "seahorse")==0)
1079 		{
1080 			d.damage *= MULT_DMG_SEAHORSEARMOR;
1081 		}
1082 	}
1083 
1084 	if (dsq->continuity.form == FORM_FISH)
1085 	{
1086 		d.damage *= MULT_DMG_FISHFORM;
1087 	}
1088 
1089 	if ((core->isNested() && dsq->game->invincibleOnNested) || dsq->game->invinciblity)
1090 	{
1091 		d.damage = 0;
1092 		d.damageType = DT_NONE;
1093 		return;
1094 	}
1095 
1096 	if (d.damageType == DT_ENEMY_INK)
1097 	{
1098 		setBlind(d.effectTime);
1099 		return;
1100 	}
1101 
1102 	if (d.damageType == DT_ENEMY_POISON)
1103 	{
1104 		dsq->continuity.setPoison(1, d.effectTime);
1105 	}
1106 
1107 	if (dsq->continuity.defenseMultTimer.isActive())
1108 	{
1109 		d.damage *= dsq->continuity.defenseMult;
1110 	}
1111 
1112 	if (dsq->continuity.invincibleTimer.isActive())
1113 		d.damage = 0;
1114 
1115 	if (!canDie)
1116 	{
1117 		if ((health - d.damage) <= 0)
1118 		{
1119 			float i = d.damage;
1120 			while (i >= 0)
1121 			{
1122 				if ((health - i) > 0)
1123 				{
1124 					d.damage = i;
1125 					break;
1126 				}
1127 
1128 				i -= 0.5f;
1129 			}
1130 		}
1131 	}
1132 
1133 	if ((!invincible || !dsq->game->invincibleOnNested) && !(invincibleBreak && damageTimer.isActive() && d.useTimer) && !dsq->continuity.invincibleTimer.isActive())
1134 	{
1135 		if (d.damageType == DT_ENEMY_ACTIVEPOISON)
1136 			core->sound->playSfx("Poison");
1137 		else
1138 			core->sound->playSfx("Pain");
1139 
1140 
1141 		setHeadTexture("Pain", 1);
1142 
1143 		int r = (rand()%2)+1;
1144 		std::ostringstream os;
1145 		os << "basicHit" << r;
1146 		skeletalSprite.transitionAnimate(os.str(), 0.05, 0, ANIMLAYER_OVERRIDE);
1147 
1148 		/*
1149 		if (d.attacker)
1150 		{
1151 			// this will probably cause a crash!
1152 			state.lookAtEntity = d.attacker;
1153 		}
1154 		*/
1155 
1156 		if (d.damage > 0)
1157 		{
1158 			float healthWillBe = health-d.damage;
1159 			// determines length of shader blur as well
1160 			float t = 0.5;
1161 			if (healthWillBe<=0)
1162 				t = 2;
1163 
1164 			dsq->rumble(d.damage, d.damage, 0.4);
1165 			if (d.damage > 0)
1166 			{
1167 				//dsq->shakeCamera(5, t);
1168 				if (d.damage >= 1)
1169 				{
1170 					float shake = d.damage*2;
1171 					if (shake > 10)
1172 						shake = 10;
1173 					dsq->shakeCamera(shake, t);
1174 				}
1175 
1176 				if (healthWillBe <= 2 && d.damageType != DT_ENEMY_ACTIVEPOISON)
1177 				{
1178 					//if (!dsq->gameSpeed.isInterpolating() && dsq->gameSpeed.x==1)
1179 
1180 					{
1181 						dsq->gameSpeed.stop();
1182 						dsq->gameSpeed.stopPath();
1183 						dsq->gameSpeed.x = 1;
1184 
1185 						dsq->overlayRed->alpha.ensureData();
1186 						dsq->overlayRed->alpha.data->path.clear();
1187 						dsq->overlayRed->alpha.data->path.addPathNode(0, 0);
1188 						dsq->overlayRed->alpha.data->path.addPathNode(1.0, 0.1);
1189 						dsq->overlayRed->alpha.data->path.addPathNode(0, 1.0);
1190 						dsq->overlayRed->alpha.startPath(1);
1191 
1192 						dsq->sound->playSfx("heartbeat");
1193 
1194 						if (healthWillBe < 2 && healthWillBe >= 1 && !dsq->game->hasPlayedLow)
1195 						{
1196 							dsq->emote.playSfx(EMOTE_NAIJALOW);
1197 							dsq->game->hasPlayedLow = 1;
1198 						}
1199 
1200 
1201 						dsq->gameSpeed.ensureData();
1202 						dsq->gameSpeed.data->path.clear();
1203 						dsq->gameSpeed.data->path.addPathNode(1, 0);
1204 						dsq->gameSpeed.data->path.addPathNode(0.25, 0.1);
1205 						dsq->gameSpeed.data->path.addPathNode(0.25, 0.4);
1206 						dsq->gameSpeed.data->path.addPathNode(1.0, 1.0);
1207 
1208 						dsq->gameSpeed.startPath(2);
1209 
1210 						//dsq->gameSpeed.interpolateTo(0.7, 3);
1211 					}
1212 					//dsq->emote();
1213 				}
1214 			}
1215 			hitEmitter.load("NaijaHit");
1216 			hitEmitter.start();
1217 
1218 			playHitSound();
1219 		}
1220 	}
1221 }
1222 
playHitSound()1223 void Avatar::playHitSound()
1224 {
1225 	int hitSound = (rand()%8)+1;
1226 	static int lastHitSound = 0;
1227 	if (lastHitSound == hitSound)
1228 	{
1229 		hitSound ++;
1230 		if (hitSound > 8)
1231 			hitSound = 1;
1232 	}
1233 	std::ostringstream os;
1234 	os << "hit" << hitSound;
1235 	core->sound->playSfx(os.str());
1236 }
1237 
1238 const int beatHealth = 3;
updateHeartbeatSfx(float t)1239 void Avatar::updateHeartbeatSfx(float t)
1240 {
1241 	/*
1242 	if (heartbeat)
1243 	{
1244 		BASS_CHANNELINFO info;
1245 		BASS_ChannelGetInfo(heartbeat, &info);
1246 		int num = (beatHealth - health);
1247 		float wantFreq = 1000 + num*300;
1248 		float useFreq = ((wantFreq*info.freq)/1000.0f);
1249 		float vol = 75 + (num*25)*0.5f;
1250 		vol *= (core->sound->getUseSfxVol()/100.0f);
1251 		//int vol = 100;
1252 		BASS_ChannelSlideAttributes(heartbeat, useFreq, vol, -101, 1000.0f*t);
1253 	}
1254 	*/
1255 }
1256 
onHealthChange(float change)1257 void Avatar::onHealthChange(float change)
1258 {
1259 	updateDamageVisualEffects();
1260 
1261 	if (health <= beatHealth && health > 0)
1262 	{
1263 		/*
1264 		if (!heartbeat)
1265 		{
1266 			//debugLog("starting heartbeat");
1267 			heartbeat = core->sound->playSfx("Heartbeat", 255, 0, 1000, 1);
1268 			//core->sound->playSfx("Heartbeat");
1269 		}
1270 		*/
1271 		updateHeartbeatSfx(0.5);
1272 	}
1273 	if (health > beatHealth)
1274 	{
1275 		/*
1276 		if (heartbeat)
1277 		{
1278 			//debugLog("stopping heartbeat");
1279 			BASS_CHANNELINFO info;
1280 			BASS_ChannelGetInfo(heartbeat, &info);
1281 			BASS_ChannelSlideAttributes(heartbeat, info.freq, -2, -101, 1000*2);
1282 			heartbeat = 0;
1283 		}
1284 		*/
1285 	}
1286 }
1287 
revive()1288 void Avatar::revive()
1289 {
1290 	entityDead	= false;
1291 	health		= 0;
1292 	heal(maxHealth);
1293 }
1294 
updateDualFormChargeEffects()1295 void Avatar::updateDualFormChargeEffects()
1296 {
1297 }
1298 
lostTarget(int i,Entity * e)1299 void Avatar::lostTarget(int i, Entity *e)
1300 {
1301 	dsq->sound->playSfx("target-unlock");
1302 }
1303 
entityDied(Entity * e)1304 void Avatar::entityDied(Entity *e)
1305 {
1306 	Entity::entityDied(e);
1307 	for (int i = 0; i < targets.size(); i++)
1308 	{
1309 		if (targets[i].e == e)
1310 		{
1311 			lostTarget(i, 0);
1312 			targets[i].e = 0;
1313 			targetUpdateDelay = 100;
1314 			targets.clear();
1315 			break;
1316 		}
1317 	}
1318 
1319 	if (state.lookAtEntity==e)
1320 		state.lookAtEntity = 0;
1321 
1322 	// eating
1323 	if (e->isGoingToBeEaten())
1324 	{
1325 		EatType et = e->getEatType();
1326 		switch(et)
1327 		{
1328 			case EAT_FILE:
1329 			{
1330 				dsq->continuity.eatBeast(e->eatData);
1331 			}
1332 			break;
1333 		}
1334 	}
1335 
1336 	//debugLog("Entity died");
1337 	//e->lastDamage.damageType == DT_AVATAR_ENERGYBLAST &&
1338 	//debugLog("Entity died");
1339 	if (e->lastDamage.form == FORM_DUAL && e->lastDamage.damageType == DT_AVATAR_SHOCK)
1340 	{
1341 		dsq->continuity.dualFormCharge ++;
1342 		updateDualFormChargeEffects();
1343 		dsq->spawnParticleEffect("SpiritSteal", e->position);
1344 		//dsq->spawnParticleEffect("SpiritBeacon", position);
1345 		core->sound->playSfx("DualForm-Absorb");
1346 		if (dsq->continuity.dualFormCharge == requiredDualFormCharge)
1347 			core->sound->playSfx("DualForm-Charge");
1348 	}
1349 	/*
1350 	std::ostringstream os;
1351 	os << "lastDamage.form = " << e->lastDamage.form;
1352 	debugLog(os.str());
1353 	*/
1354 }
1355 
enableInput()1356 void Avatar::enableInput()
1357 {
1358 	ActionMapper::enableInput();
1359 	dsq->game->toggleMiniMapRender(1);
1360 
1361 	if (!dsq->game->isApplyingState())
1362 		dsq->toggleCursor(true);
1363 
1364 	if (movingOn)
1365 	{
1366 		dsq->setMousePosition(Vector(400,300));
1367 	}
1368 
1369 	if (dsq->continuity.form == FORM_ENERGY)
1370 	{
1371 		for (int i = 0; i < targetQuads.size(); i++)
1372 			targetQuads[i]->start();
1373 	}
1374 
1375 	setInvincible(false);
1376 	// can't do that here, cause it'll break the hug
1377 	//stillTimer.stop();
1378 }
1379 
disableInput()1380 void Avatar::disableInput()
1381 {
1382 	ActionMapper::disableInput();
1383 
1384 	// can't do that here, cause it'll break the hug
1385 	//stillTimer.stop();
1386 
1387 	closeSingingInterface();
1388 	dsq->game->toggleMiniMapRender(0);
1389 	dsq->toggleCursor(false);
1390 	endCharge();
1391 	clearTargets();
1392 	if (movingOn)
1393 	{
1394 		dsq->setMousePosition(Vector(400,300));
1395 	}
1396 
1397 	for (int i = 0; i < targetQuads.size(); i++)
1398 	{
1399 		targetQuads[i]->stop();
1400 	}
1401 
1402 	setInvincible(true);
1403 }
1404 
clearTargets()1405 void Avatar::clearTargets()
1406 {
1407 	for (int i = 0; i < targets.size(); i++)
1408 	{
1409 		if (targets[i].e)
1410 		{
1411 			lostTarget(i, 0);
1412 		}
1413 		targets[i].e = 0;
1414 	}
1415 }
1416 
openSingingInterface()1417 void Avatar::openSingingInterface()
1418 {
1419 	if (!singing && health > 0 && !isEntityDead() && !blockSinging)
1420 	{
1421 		//core->mouse.position = Vector(400,300);
1422 		if (dsq->inputMode != INPUT_MOUSE)
1423 		{
1424 			core->centerMouse();
1425 			//core->setMousePosition(Vector(400,300));
1426 		}
1427 
1428 		core->setMouseConstraintCircle(core->center, singingInterfaceRadius);
1429 		stopRoll();
1430 		singing = true;
1431 		currentSongIdx = SONG_NONE;
1432 
1433 		// make the singing icons appear
1434 		for (int i = 0; i < songIcons.size(); i++)
1435 		{
1436 			songIcons[i]->openInterface();
1437 		}
1438 		currentSong.notes.clear();
1439 
1440 		songInterfaceTimer = 0;
1441 
1442 		dsq->game->songLineRender->clear();
1443 
1444 
1445 		if (dsq->inputMode == INPUT_JOYSTICK)
1446 		{
1447 			core->setMousePosition(core->center);
1448 		}
1449 	}
1450 }
1451 
closeSingingInterface()1452 void Avatar::closeSingingInterface()
1453 {
1454 
1455 	if (dsq->game->songLineRender)
1456 		dsq->game->songLineRender->clear();
1457 	if (singing)
1458 	{
1459 		core->setMouseConstraint(false);
1460 		usingDigital = false;
1461 		quickSongCastDelay = 1;
1462 
1463 		// HACK: this prevents being "locked" away from the seahorse... so naija can
1464 		// be in singing range of the seahorse
1465 		applyRidingPosition();
1466 		singing = false;
1467 
1468 		for (int i = 0; i < songIcons.size(); i++)
1469 		{
1470 			songIcons[i]->closeInterface();
1471 		}
1472 
1473 		if (dsq->continuity.form == FORM_NORMAL)
1474 			setHeadTexture("");
1475 
1476 		currentSongIdx = dsq->continuity.checkSongAssisted(currentSong);
1477 		if (currentSongIdx != SONG_NONE)
1478 		{
1479 			dsq->continuity.castSong(currentSongIdx);
1480 			currentSongIdx = SONG_NONE;
1481 		}
1482 	}
1483 }
1484 
toggleCape(bool on)1485 void Avatar::toggleCape(bool on)
1486 {
1487 	if (!hair) return;
1488 
1489 	if (!on)
1490 		hair->alphaMod = 0;
1491 	else
1492 		hair->alphaMod = 1;
1493 }
1494 
refreshDualFormModel()1495 void Avatar::refreshDualFormModel()
1496 {
1497 	//charging = 0;
1498 	if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
1499 		refreshModel("Naija", "DualForm_Naija");
1500 	else if (dsq->continuity.dualFormMode == Continuity::DUALFORM_LI)
1501 		refreshModel("Naija", "DualForm_Li");
1502 }
1503 
updateDualFormGlow(float dt)1504 void Avatar::updateDualFormGlow(float dt)
1505 {
1506 	if (dsq->continuity.form == FORM_DUAL && bone_dualFormGlow)
1507 	{
1508 
1509 		float perc = 1;
1510 		if (requiredDualFormCharge != 0)
1511 			perc = float(dsq->continuity.dualFormCharge)/float(requiredDualFormCharge);
1512 		if (perc > 1)
1513 			perc = 1;
1514 		bone_dualFormGlow->alpha = perc*0.5f + 0.1f;
1515 		bone_dualFormGlow->scale.interpolateTo(Vector(perc, perc), 0.2);
1516 	}
1517 }
1518 
changeForm(FormType form,bool effects,bool onInit,FormType lastForm)1519 void Avatar::changeForm(FormType form, bool effects, bool onInit, FormType lastForm)
1520 {
1521 	/*
1522 	if (core->afterEffectManager)
1523 		core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter, 0.1,0.03,15,0.2f, 0.5));
1524 	*/
1525 
1526 
1527 	/*
1528 	if (pullTarget)
1529 	{
1530 		pullTarget->stopPull();
1531 		pullTarget = 0;
1532 	}
1533 	*/
1534 
1535 	if (form == FORM_DUAL && !dsq->continuity.hasLi())
1536 		return;
1537 
1538 	if (!canChangeForm) return;
1539 
1540 	std::ostringstream os;
1541 	os << "changeForm: " << form;
1542 	debugLog(os.str());
1543 
1544 	/*
1545 	if (dsq->game)
1546 		dsq->game->clearControlHint();
1547 	*/
1548 
1549 	if (lastForm == FORM_NONE)
1550 		lastForm = dsq->continuity.form;
1551 
1552 	endCharge();
1553 
1554 	std::ostringstream os2;
1555 	os2 << "lastForm: " << lastForm;
1556 	debugLog(os2.str());
1557 
1558 	for (int i = 0; i < targetQuads.size(); i++)
1559 	{
1560 		if (targetQuads[i])
1561 			targetQuads[i]->stop();
1562 	}
1563 
1564 
1565 	if (bone_dualFormGlow)
1566 		bone_dualFormGlow->scale = 0;
1567 
1568 	clearTargets();
1569 
1570 	if (form != FORM_NORMAL)
1571 		stopAura();
1572 
1573 	switch (lastForm)
1574 	{
1575 	case FORM_FISH:
1576 	{
1577 		// check nearby area
1578 		//bool isTooCloseToWall=false;
1579 
1580 		if (isNearObstruction(3))
1581 		{
1582 			Vector n = dsq->game->getWallNormal(position);
1583 			if (!n.isZero())
1584 			{
1585 				n *= 400;
1586 				vel += n;
1587 			}
1588 
1589 			return;
1590 		}
1591 		//rotationOffset.interpolateTo(Vector(0,0,0), 0.5);
1592 
1593 		collideRadius = COLLIDE_RADIUS_NORMAL;
1594 		setCanLockToWall(true);
1595 		setCollisionAvoidanceData(COLLIDE_RANGE_NORMAL, COLLIDE_MOD_NORMAL);
1596 	}
1597 	break;
1598 	case FORM_SUN:
1599 		lightFormGlow->alpha.interpolateTo(0, 0.5);
1600 		lightFormGlowCone->alpha.interpolateTo(0, 0.5);
1601 	break;
1602 	case FORM_SPIRIT:
1603 		//position.interpolateTo(bodyPosition, 2, 0);
1604 		position = bodyPosition;
1605 		dsq->continuity.warpLiToAvatar();
1606 		spiritBeaconEmitter.start();
1607 		setCanActivateStuff(true);
1608 		setCanLockToWall(true);
1609 		setCanBurst(true);
1610 		setDamageTarget(DT_WALLHURT, true);
1611 	break;
1612 	case FORM_BEAST:
1613 		setCanSwimAgainstCurrents(false);
1614 	break;
1615 	case FORM_DUAL:
1616 		if (dsq->continuity.hasLi())
1617 		{
1618 			dsq->game->li->alpha = 1;
1619 			dsq->game->li->position = position;
1620 			dsq->game->li->setState(STATE_IDLE);
1621 		}
1622 	break;
1623 	case FORM_NATURE:
1624 		setDamageTarget(DT_WALLHURT, true);
1625 		break;
1626 	default:
1627 		if (leftHandEmitter && rightHandEmitter)
1628 		{
1629 			leftHandEmitter->stop();
1630 			rightHandEmitter->stop();
1631 		}
1632 	break;
1633 	}
1634 
1635 	elementEffectMult = 1;
1636 	state.abilityDelay = 0;
1637 	formAbilityDelay = 0;
1638 	dsq->continuity.form = form;
1639 	formTimer = 0;
1640 	if (effects)
1641 	{
1642 		if (core->afterEffectManager)
1643 			core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
1644 
1645 		switch(form)
1646 		{
1647 		case FORM_ENERGY:
1648 			core->sound->playSfx("EnergyForm");
1649 			/*
1650 			dsq->game->tintColor.path.addPathNode(Vector(1,1,1),0);
1651 			dsq->game->tintColor.path.addPathNode(Vector(1.5,1.5,4),0.25);
1652 			dsq->game->tintColor.path.addPathNode(Vector(4,1.5,1),0.5);
1653 			dsq->game->tintColor.path.addPathNode(Vector(1,1,1),0.5);
1654 			dsq->game->tintColor.startPath(2);
1655 			*/
1656 
1657 			/*
1658 			dsq->game->tintColor = Vector(1,1,3);
1659 			dsq->game->tintColor.interpolateTo(Vector(1,1,1), 1);
1660 			*/
1661 
1662 		break;
1663 		case FORM_NORMAL:
1664 			core->sound->playSfx("NormalForm");
1665 		break;
1666 		case FORM_BEAST:
1667 			core->sound->playSfx("BeastForm");
1668 		break;
1669 		case FORM_FISH:
1670 			core->sound->playSfx("FishForm");
1671 		break;
1672 		case FORM_SUN:
1673 			core->sound->playSfx("SunForm");
1674 		break;
1675 		case FORM_NATURE:
1676 			core->sound->playSfx("NatureForm");
1677 		break;
1678 		case FORM_SPIRIT:
1679 			spiritBeaconEmitter.start();
1680 		break;
1681 		case FORM_DUAL:
1682 			core->sound->playSfx("DualForm");
1683 		break;
1684 		}
1685 
1686 		/*
1687 		dsq->overlay->color = Vector(1,1,1);
1688 		dsq->overlay->alpha.interpolateTo(1.0, 0.2);
1689 		avatar->disableInput();
1690 		setv(EV_NOINPUTNOVEL, 0);
1691 		core->main(0.2);
1692 		setv(EV_NOINPUTNOVEL, 1);
1693 		dsq->overlay->alpha.interpolateTo(0, 0.2);
1694 		dsq->overlay->color.interpolateTo(0, 0.4);
1695 		*/
1696 		dsq->overlay->color = Vector(1,1,1);
1697 		dsq->overlay->alpha = 1;
1698 		dsq->overlay->alpha.interpolateTo(0, 0.5);
1699 		dsq->overlay->color.interpolateTo(0, 1.0);
1700 	}
1701 	/*
1702 	if (form != FORM_ENERGY)
1703 	{
1704 		dsq->game->sceneColor3.interpolateTo(Vector(1,1,1), 0.2);
1705 	}
1706 	*/
1707 	float lastHairAlphaMod = 0;
1708 	if (hair)
1709 	{
1710 		hair->alphaMod = 0;
1711 		lastHairAlphaMod = hair->alphaMod;
1712 	}
1713 	switch (form)
1714 	{
1715 	case FORM_ENERGY:
1716 		refreshModel("Naija", "EnergyForm");
1717 		for (int i = 0; i < targetQuads.size(); i++)
1718 			targetQuads[i]->start();
1719 		leftHandEmitter->load("EnergyFormHandGlow");
1720 		leftHandEmitter->start();
1721 		rightHandEmitter->load("EnergyFormHandGlow");
1722 		rightHandEmitter->start();
1723 	break;
1724 	case FORM_FISH:
1725 	{
1726 		fallOffWall();
1727 
1728 		setBoneLock(BoneLock());
1729 
1730 		refreshModel("FishForm", "");
1731 		//rotationOffset.interpolateTo(Vector(0,0,-90), 0.5);
1732 		//refreshModel("NaijaFish", "");
1733 
1734 		collideRadius = COLLIDE_RADIUS_FISH;
1735 		setCanLockToWall(false);
1736 		setCollisionAvoidanceData(COLLIDE_RANGE_FISH, COLLIDE_MOD_FISH);
1737 		elementEffectMult = 0.4f;
1738 	}
1739 	break;
1740 	case FORM_SUN:
1741 	{
1742 		refreshModel("Naija", "SunForm");
1743 		lightFormGlow->moveToFront();
1744 		lightFormGlow->alpha.interpolateTo(0.75, 1);
1745 		lightFormGlowCone->alpha.interpolateTo(0.4, 1);
1746 
1747 		lightFormGlow->alphaMod = 0;
1748 		lightFormGlowCone->alphaMod = 0;
1749 	}
1750 	break;
1751 	case FORM_NORMAL:
1752 	{
1753 		if (lastForm == FORM_SPIRIT)
1754 		{
1755 			dsq->continuity.shiftWorlds();
1756 			fallOffWall();
1757 		}
1758 		refreshNormalForm();
1759 		//skeletalSprite.loadSkeletal("child");
1760 	}
1761 	break;
1762 	case FORM_NATURE:
1763 		refreshModel("Naija", "NatureForm");
1764 		if (hair)
1765 		{
1766 			hair->setTexture("Naija/Cape-NatureForm");
1767 			hair->alphaMod = 1.0;
1768 		}
1769 		setDamageTarget(DT_WALLHURT, false);
1770 
1771 	break;
1772 	case FORM_BEAST:
1773 	{
1774 		refreshModel("Naija", "BeastForm");
1775 		setCanSwimAgainstCurrents(true);
1776 	}
1777 	break;
1778 	case FORM_SPIRIT:
1779 		bodyPosition = position;
1780 		bodyOffset = offset;
1781 		fallOffWall();
1782 		dsq->continuity.shiftWorlds();
1783 		setCanActivateStuff(false);
1784 		setCanLockToWall(false);
1785 		setCanBurst(false);
1786 		setDamageTarget(DT_WALLHURT, false);
1787 		elementEffectMult = 0;
1788 
1789 		if (onInit)
1790 		{
1791 			skeletalSprite.alphaMod = 0;
1792 			canChangeForm = false;
1793 			useSpiritDistance = false;
1794 			inSpiritWorld = true;
1795 		}
1796 		/*
1797 		if (hair)
1798 			hair->alphaMod = lastHairAlphaMod;
1799 		*/
1800 	break;
1801 	case FORM_DUAL:
1802 	{
1803 		if (dsq->continuity.hasLi())
1804 		{
1805 			dsq->game->li->setState(STATE_WAIT);
1806 			dsq->game->li->alpha = 0;
1807 		}
1808 		//dualFormMode = DUALFORM_LI;
1809 		refreshDualFormModel();
1810 		/*
1811 		for (int i = 0; i < targetQuads.size(); i++)
1812 			targetQuads[i]->start();
1813 		*/
1814 	}
1815 	break;
1816 	default:
1817 	break;
1818 	}
1819 	setHeadTexture("");
1820 	if (effects)
1821 		avatar->enableInput();
1822 
1823 	//if (onInit) {
1824 		//idle();//skeletalSprite.animate("idle", -1, 0);
1825 	//}
1826 }
1827 
getLastNote()1828 int Avatar::getLastNote()
1829 {
1830 	return lastNote;
1831 }
1832 
singNote(int note)1833 void Avatar::singNote(int note)
1834 {
1835 	currentSong.notes.push_back(note);
1836 	lastNote = note;
1837 }
1838 
updateSingingInterface(float dt)1839 void Avatar::updateSingingInterface(float dt)
1840 {
1841 	if (songIcons.size()>0 && songIcons[0]->alpha.x > 0)
1842 	{
1843 		if (dsq->inputMode != INPUT_JOYSTICK && !core->mouse.change.isZero())
1844 		{
1845 			if (dsq->game->songLineRender && songIcons[0]->alpha.x == 1)
1846 			{
1847 				float smallestDist = HUGE_VALF;
1848 				int closest = -1;
1849 				for (int i = 0; i < songIcons.size(); i++)
1850 				{
1851 					float dist = (songIcons[i]->position - core->mouse.position).getSquaredLength2D();
1852 					if (dist < smallestDist)
1853 					{
1854 						smallestDist = dist;
1855 						closest = i;
1856 					}
1857 				}
1858 
1859 				dsq->game->songLineRender->newPoint(core->mouse.position, songIcons[closest]->noteColor);
1860 			}
1861 		}
1862 
1863 		if (health <= 0 || isEntityDead())
1864 		{
1865 			closeSingingInterface();
1866 		}
1867 		else
1868 		{
1869 			if (dsq->inputMode == INPUT_JOYSTICK)
1870 			{
1871 				Vector d = dsq->joystick.position;
1872 
1873 				if (d.isLength2DIn(JOYSTICK_NOTE_THRESHOLD))
1874 				{
1875 					core->setMousePosition(core->center);
1876 				}
1877 				else
1878 				{
1879 					// Choose the closest note based on the joystick input
1880 					// angle (rather than the resultant cursor position).
1881 					// But if we already have an active note and we're not
1882 					// within the note-accept threshold, maintain the
1883 					// current note instead.
1884 					float angle = (atan2f(-d.y, d.x) * 180 / PI) + 90;
1885 					if (angle < 0)
1886 						angle += 360;
1887 					int closestNote = (int)floorf(angle/45 + 0.5f);
1888 					float angleOffset = fabsf(angle - closestNote*45);
1889 					if (closestNote == 8)
1890 						closestNote = 0;
1891 
1892 					bool setNote = (angleOffset <= NOTE_ACCEPT_ANGLE_OFFSET);
1893 					if (!setNote)
1894 					{
1895 						bool alreadyAtNote = false;
1896 						for (int i = 0; i < songIcons.size(); i++)
1897 						{
1898 							const float dist = (songIcons[i]->position - core->mouse.position).getSquaredLength2D();
1899 							if (dist <= sqr(NOTE_ACCEPT_DISTANCE))
1900 							{
1901 								alreadyAtNote = true;
1902 								break;
1903 							}
1904 						}
1905 						if (!alreadyAtNote)
1906 							setNote = true;
1907 					}
1908 
1909 					if (setNote)
1910 						core->setMousePosition(songIcons[closestNote]->position);
1911 				}
1912 			}
1913 
1914 			setSongIconPositions();
1915 		}
1916 	}
1917 }
1918 
setSongIconPositions()1919 void Avatar::setSongIconPositions()
1920 {
1921 	float radIncr = (2*PI)/float(songIcons.size());
1922 	float rad = 0;
1923 	for (int i = 0; i < songIcons.size(); i++)
1924 	{
1925 		songIcons[i]->position = Vector(400,300)+/*this->position + */Vector(sinf(rad)*singingInterfaceRadius, cosf(rad)*singingInterfaceRadius);
1926 		rad += radIncr;
1927 	}
1928 }
1929 
1930 const int chkDist = 2500*2500;
1931 
getNearestTarget(const Vector & checkPos,const Vector & distPos,Entity * source,DamageType dt,bool override,std::vector<Target> * ignore,EntityList * entityList)1932 Target Avatar::getNearestTarget(const Vector &checkPos, const Vector &distPos, Entity *source, DamageType dt, bool override, std::vector<Target> *ignore, /*FIXME:unused*/ EntityList *entityList)
1933 {
1934 	BBGE_PROF(Avatar_getNearestTarget);
1935 	Target t;
1936 
1937 	Vector targetPosition;
1938 	int targetPt = -1;
1939 	Entity *closest = 0;
1940 	int highestPriority = -999;
1941 	float smallestDist = HUGE_VALF;
1942 	Entity *e = 0;
1943 	FOR_ENTITIES(i)
1944 	{
1945 		e = *i;
1946 		/*
1947 		int j;
1948 		for (j = 0; j < targets.size(); j++)
1949 		{
1950 			if (targets[j].e == e) break;
1951 		}
1952 		if (j != targets.size()) continue;
1953 		*/
1954 
1955 		//e &&
1956 		if (e != this && e->targetPriority >= highestPriority && this->pullTarget != e && e->isDamageTarget(dt) && dsq->game->isValidTarget(e, this))
1957 		{
1958 
1959 
1960 
1961 			if (e->position.isNan())
1962 			//if (false)
1963 			{
1964 				std::ostringstream os;
1965 				os << "NAN position entity name: " << e->name << " type: " << e->getEntityType();
1966 				debugLog(os.str());
1967 				continue;
1968 			}
1969 			else
1970 			{
1971 				int dist = (e->position - position).getSquaredLength2D();
1972 				if (dist < chkDist)
1973 				{
1974 					int numTargetPoints = e->getNumTargetPoints();
1975 					bool clearAfter = false;
1976 					if (numTargetPoints == 0)
1977 					{
1978 						if (ignore)
1979 						{
1980 							int j = 0;
1981 							for (; j < ignore->size(); j++)
1982 							{
1983 								if ((*ignore)[j].e == e)
1984 									break;
1985 							}
1986 							if (j != ignore->size()) continue;
1987 						}
1988 						e->addTargetPoint(e->getEnergyShotTargetPosition());
1989 						clearAfter = true;
1990 						numTargetPoints = 1;
1991 					}
1992 					if (numTargetPoints > 0)
1993 					{
1994 						for (int i = 0; i < numTargetPoints; i++)
1995 						{
1996 							if (ignore)
1997 							{
1998 								int j = 0;
1999 								for (; j < ignore->size(); j++)
2000 								{
2001 									if ((*ignore)[j].e == e && (*ignore)[j].targetPt == i)
2002 										break;
2003 								}
2004 								if (j != ignore->size()) continue;
2005 							}
2006 							float dist = (e->getTargetPoint(i) - distPos).getSquaredLength2D();
2007 							//float dist = (e->getTargetPoint(i) - distPos).getLength2D();
2008 							if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
2009 							{
2010 								if (override || (checkPos - e->getTargetPoint(i)).isLength2DIn(64))
2011 								{
2012 									dist = (e->getTargetPoint(i) - checkPos).getSquaredLength2D();
2013 									if (dist < smallestDist)
2014 									{
2015 										highestPriority = e->targetPriority;
2016 										targetPosition = e->getTargetPoint(i);
2017 										closest = e;
2018 										smallestDist = dist;
2019 										targetPt = i;
2020 									}
2021 								}
2022 							}
2023 						}
2024 					}
2025 					if (clearAfter)
2026 						e->clearTargetPoints();
2027 				}
2028 			}
2029 		}
2030 	}
2031 	t.e = closest;
2032 	t.pos = targetPosition;
2033 	t.targetPt = targetPt;
2034 	return t;
2035 }
2036 
2037 float maxTargetDelay = 0.5;
2038 bool wasDown = false;
updateTargets(float dt,bool override)2039 void Avatar::updateTargets(float dt, bool override)
2040 {
2041 	DamageType damageType = DT_AVATAR_ENERGYBLAST;
2042 	for (int i = 0; i < targets.size(); i++)
2043 	{
2044 		if (!targets[i].e
2045 		|| !targets[i].e->isPresent()
2046 		|| targets[i].e->getState() == STATE_DEATHSCENE
2047 		|| !dsq->game->isValidTarget(targets[i].e, this))
2048 		{
2049 			targets.clear();
2050 			break;
2051 		}
2052 	}
2053 	if ((dsq->inputMode == INPUT_MOUSE || dsq->inputMode == INPUT_KEYBOARD) && !(wasDown && core->mouse.buttons.right))
2054 	{
2055 		wasDown = false;
2056 		float mod = 1;
2057 		if (isCharging())
2058 			mod = maxTargetDelay*10;
2059 		targetUpdateDelay += dt*mod;
2060 	}
2061 
2062 	if (targetUpdateDelay > maxTargetDelay || override)
2063 	{
2064 		maxTargetDelay = 0;
2065 		std::vector<Target> oldTargets = targets;
2066 		if ((dsq->continuity.form == FORM_ENERGY) && ((core->mouse.buttons.right && state.spellCharge > 0.3f) || override))
2067 			//&& state.spellCharge > 0.2f /*&& state.spellCharge < 0.5f*/
2068 		{
2069 			// crappy hack for now, assuming one target:
2070 			targets.clear();
2071 
2072 
2073 			Vector dir = getAim();
2074 			Vector checkPos = position + dir;
2075 			Vector distPos = position;
2076 			if (!(dsq->getGameCursorPosition() - distPos).isLength2DIn(4))
2077 			{
2078 				Target t;
2079 				t = getNearestTarget(checkPos, distPos, this, damageType, override, &targets);
2080 				if (t.e)
2081 				{
2082 					//if ((t.getWorldPosition() - dsq->getGameCursorPosition()).isLength2DIn(64))
2083 					{
2084 
2085 
2086 						// found a target?
2087 						targets.push_back(t);
2088 
2089 						targetUpdateDelay = 0;
2090 						if (!override && core->mouse.buttons.right)
2091 						{
2092 							maxTargetDelay = 90;
2093 
2094 							dsq->spawnParticleEffect("TargetAquired", t.pos);
2095 							wasDown = true;
2096 						}
2097 					}
2098 				}
2099 			}
2100 			if (targets.empty())
2101 			{
2102 				for (int i = 0; i < oldTargets.size(); i++)
2103 				{
2104 					Entity *e = oldTargets[i].e;
2105 					if (e)
2106 					{
2107 						int dist = (e->getTargetPoint(oldTargets[i].targetPt) - distPos).getSquaredLength2D();
2108 						if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
2109 						{
2110 							targets.push_back(oldTargets[i]);
2111 						}
2112 					}
2113 					else
2114 					{
2115 						targets.clear();
2116 						break;
2117 					}
2118 				}
2119 			}
2120 		}
2121 	}
2122 	else
2123 	{
2124 		for (int i = 0; i < targets.size(); i++)
2125 		{
2126 			Entity *e = targets[i].e;
2127 			if (e)
2128 			{
2129 				if (!(position - e->position).isLength2DIn(e->getTargetRange() + TARGET_RANGE + TARGET_GRACE_RANGE) || !dsq->game->isValidTarget(e, this) || !e->isDamageTarget(damageType))
2130 				{
2131 					lostTarget(i, targets[i].e);
2132 					targets[i].e = 0;
2133 					targetUpdateDelay = maxTargetDelay;
2134 					wasDown = false;
2135 				}
2136 			}
2137 		}
2138 	}
2139 }
2140 
loseTargets()2141 void Avatar::loseTargets()
2142 {
2143 	for (int i = 0; i < targets.size(); i++)
2144 	{
2145 		Entity *e = targets[i].e;
2146 		if (e)
2147 		{
2148 			lostTarget(i, targets[i].e);
2149 			targets[i].e = 0;
2150 			targetUpdateDelay = maxTargetDelay;
2151 		}
2152 	}
2153 }
2154 
updateTargetQuads(float dt)2155 void Avatar::updateTargetQuads(float dt)
2156 {
2157 
2158 	const Vector cursorpos = dsq->getGameCursorPosition();
2159 	particleManager->setSuckPosition(1, cursorpos);
2160 
2161 	/*
2162 	for (int i = 0; i < targetQuads.size(); i++)
2163 	{
2164 
2165 	}
2166 	*/
2167 
2168 	static Entity *lastTargetE = 0;
2169 	const float tt = 0.02;
2170 	for (int i = 0; i < targets.size(); i++)
2171 	{
2172 		if (targets[i].e)
2173 		{
2174 
2175 			targetQuads[i]->alpha.interpolateTo(1, 0.1);
2176 			Entity *e = targets[i].e;
2177 			if (lastTargetE != e)
2178 			{
2179 				dsq->sound->playSfx("target-lock");
2180 				lastTargetE = e;
2181 			}
2182 			else
2183 			{
2184 				//targetQuads[i]->position.interpolateTo(targets[i].pos, 0.01);
2185 			}
2186 			targetQuads[i]->position.interpolateTo(targets[i].pos, tt);
2187 			targets[i].pos = e->getTargetPoint(targets[i].targetPt);
2188 			if (i == 0)
2189 			{
2190 				particleManager->setSuckPosition(1, targets[i].pos); // suckpos 1 is overridden elsewhere later
2191 				particleManager->setSuckPosition(2, targets[i].pos);
2192 			}
2193 
2194 			/*
2195 			Emitter *em = targetQuads[i];
2196 			if (!em->isRunning())
2197 			{
2198 				em->start();
2199 			}
2200 			*/
2201 		}
2202 		else
2203 		{
2204 			targetQuads[i]->position = cursorpos;
2205 			//targetQuads[i]->alpha.interpolateTo(0, 0.1);
2206 		}
2207 	}
2208 
2209 	if (targets.empty())
2210 	{
2211 		for (int i = 0; i < targetQuads.size(); i++)
2212 		{
2213 			if (lastTargetE != 0)
2214 			{
2215 				lastTargetE = 0;
2216 			}
2217 			//targetQuads[i]->position.interpolateTo(dsq->getGameCursorPosition(),tt);
2218 			/*
2219 			std::ostringstream os;
2220 			os << "setting targetQuads[i] to game cursor, is running = " << targetQuads[i]->isRunning();
2221 			debugLog(os.str());
2222 			*/
2223 
2224 			targetQuads[i]->position = cursorpos;
2225 			if (dsq->continuity.form == FORM_ENERGY && isInputEnabled())
2226 			{
2227 				if (dsq->inputMode == INPUT_JOYSTICK && targetQuads[i]->isRunning())
2228 				{
2229 					targetQuads[i]->stop();
2230 				}
2231 				else if (dsq->inputMode != INPUT_JOYSTICK && !targetQuads[i]->isRunning())
2232 				{
2233 					targetQuads[i]->start();
2234 				}
2235 			}
2236 
2237 			/*
2238 			if (targetQuads[i]->isRunning())
2239 			{
2240 				targetQuads[i]->stop();
2241 			}
2242 			*/
2243 		}
2244 	}
2245 }
2246 
2247 //fireAtNearestValidEntity("Fire", DT_AVATAR_ENERGYBLAST);
fireAtNearestValidEntity(const std::string & shot)2248 bool Avatar::fireAtNearestValidEntity(const std::string &shot)
2249 {
2250 	if (state.swimTimer > 0)
2251 		state.swimTimer -= 0.5f;
2252 
2253 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
2254 
2255 	targetUpdateDelay = 0;
2256 	if (targetUpdateDelay < 0)
2257 		targetUpdateDelay = 0;
2258 	//bool big = false;
2259 
2260 	Vector dir;
2261 	Vector p = position;
2262 	p = boneLeftArm->getWorldPosition();
2263 	//&& !dsq->game->isObstructed(TileVector(position))
2264 	/*
2265 	if (dsq->inputMode == INPUT_MOUSE && state.lockedToWall )
2266 		dir = dsq->getGameCursorPosition() - p;
2267 	else
2268 	*/
2269 	dir = getAim();
2270 
2271 	ShotData *shotData = Shot::getShotData(shot);
2272 
2273 
2274 	bool aimAt = (dir.z == 1.0f);
2275 	//bool aimAt = true;
2276 	dir.z = 0;
2277 	Vector targetPosition;
2278 
2279 	//std::vector<Target>targets;
2280 
2281 	bool firedShot = false;
2282 	//int homing = 0;
2283 	/*
2284 	if (target)
2285 	{
2286 		if (dsq->inputMode != INPUT_JOYSTICK && vel.isLength2DIn(50))
2287 		{
2288 		}
2289 		else
2290 		{
2291 
2292 		}
2293 		homing = home;
2294 	}
2295 	else
2296 		homing = 0;
2297 	*/
2298 
2299 	/*
2300 	if (!dir.isLength2DIn(2))
2301 	{
2302 	*/
2303 	Shot *s = 0;
2304 	bool clearTargets = false;
2305 
2306 	// allow autoAim if desired
2307 	if ((dsq->inputMode == INPUT_JOYSTICK && !aimAt) || dsq->user.control.autoAim)
2308 	{
2309 		if (targets.empty())
2310 		{
2311 			// force a grab of the nearest targets
2312 			updateTargets(shotData->damageType, true);
2313 			// clear the targets after
2314 			clearTargets = true;
2315 		}
2316 	}
2317 
2318 	if (!targets.empty())
2319 	{
2320 		//homing = home;
2321 		for (int i = 0; i < targets.size(); i++)
2322 		{
2323 			/*
2324 			if (!aimAt)
2325 			{
2326 				dir = targets[i].pos - p;
2327 			}
2328 			*/
2329 				/*
2330 				std::ostringstream os;
2331 				os << "shotdir(" << dir.x << ", " << dir.y << ")";
2332 				debugLog(os.str());
2333 				*/
2334 
2335 
2336 				/*
2337 				Vector oldDir = dir;
2338 
2339 				dir.normalize2D();
2340 				dir = (dir + oldDir)/2.0f;
2341 				*/
2342 
2343 			if (!aimAt)
2344 			{
2345 				dir = (targets[i].e->getTargetPoint(targets[i].targetPt) - p);
2346 			}
2347 
2348 			s = dsq->game->fireShot(shot, this, targets[i].e);
2349 			s->setAimVector(dir);
2350 			s->setTargetPoint(targets[i].targetPt);
2351 
2352 			/*
2353 			if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2354 			{
2355 				s = dsq->game->fireShot("EnergyBlast2", this, targets[i].e);
2356 				s->setAimVector(dir);
2357 				s->setTargetPoint(targets[i].targetPt);
2358 			}
2359 			else
2360 			{
2361 				s = dsq->game->fireShot("EnergyBlast", this, targets[i].e);
2362 				s->setAimVector(dir);
2363 				s->setTargetPoint(targets[i].targetPt);
2364 			}
2365 			*/
2366 		}
2367 	}
2368 	else
2369 	{
2370 		//if (!dir.isLength2DIn(2) || dsq->inputMode == INPUT_JOYSTICK)
2371 		if (true)
2372 		{
2373 			s = dsq->game->fireShot(shot, this);
2374 
2375 			if (dir.isLength2DIn(2))
2376 			{
2377 				if (!vel.isLength2DIn(2))
2378 					s->setAimVector(vel);
2379 				else // standing still
2380 					s->setAimVector(getForwardAim());
2381 			}
2382 			else
2383 			{
2384 				s->setAimVector(dir);
2385 			}
2386 			/*
2387 			if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2388 			{
2389 				s = dsq->game->fireShot("EnergyBlast2", this);
2390 				s->setAimVector(dir);
2391 			}
2392 			else
2393 			{
2394 				s = dsq->game->fireShot("EnergyBlast", this);
2395 				s->setAimVector(dir);
2396 			}
2397 			*/
2398 		}
2399 	}
2400 
2401 	if (s)
2402 	{
2403 		checkUpgradeForShot(s);
2404 
2405 
2406 
2407 		skeletalSprite.transitionAnimate("fireBlast", 0.1, 0, ANIMLAYER_ARMOVERRIDE);
2408 		s->position = p;
2409 		//s->damageType = dt;
2410 		/*
2411 		if (!targets.empty())
2412 			s->damage = float(damage)/float(targets.size());
2413 		*/
2414 		firedShot = true;
2415 	}
2416 
2417 	if (clearTargets)
2418 	{
2419 		targets.clear();
2420 		// try to avoid targets sticking
2421 		updateTargetQuads(shotData->damageType);
2422 	}
2423 
2424 	return firedShot;
2425 }
2426 
getFacing()2427 Vector Avatar::getFacing()
2428 {
2429 	if (vel.isLength2DIn(2) && rotation.z == 0)
2430 	{
2431 		if (isfh())
2432 			return Vector(1,0);
2433 		else
2434 			return Vector(-1,0);
2435 	}
2436 	return getForward();
2437 }
2438 
switchDualFormMode()2439 void Avatar::switchDualFormMode()
2440 {
2441 	//debugLog("dualForm: changing");
2442 
2443 	dsq->sound->playSfx("dualform-switch");
2444 
2445 	dsq->overlay->color = Vector(1,1,1);
2446 	dsq->fade(1, 0);
2447 	dsq->fade(0, 0.5);
2448 
2449 	if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
2450 		dsq->continuity.dualFormMode = Continuity::DUALFORM_LI;
2451 	else
2452 		dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
2453 
2454 	refreshDualFormModel();
2455 }
2456 
hasThingToActivate()2457 bool Avatar::hasThingToActivate()
2458 {
2459 	return ((pathToActivate != 0) || (entityToActivate != 0));
2460 }
2461 
formAbility(int ability)2462 void Avatar::formAbility(int ability)
2463 {
2464 	if (hasThingToActivate()) return;
2465 	//debugLog("form ability function");
2466 	switch(dsq->continuity.form)
2467 	{
2468 	case FORM_DUAL:
2469 		{
2470 			debugLog("dual form ability");
2471 			/*
2472 			if (this->getVectorToCursorFromScreenCentre().isLength2DIn(minMouse))
2473 			{
2474 				debugLog("in and changing");
2475 				if (dualFormMode == DUALFORM_NAIJA)
2476 					dualFormMode = DUALFORM_LI;
2477 				else
2478 					dualFormMode = DUALFORM_NAIJA;
2479 				refreshDualFormModel();
2480 			}
2481 			else
2482 			*/
2483 			{
2484 				/*
2485 				if (chargeLevelAttained == 2)
2486 				{
2487 					if (dualFormMode == DUALFORM_NAIJA)
2488 						dualFormMode = DUALFORM_LI;
2489 					else
2490 						dualFormMode = DUALFORM_NAIJA;
2491 					refreshDualFormModel();
2492 				}
2493 				else
2494 				*/
2495 				{
2496 					if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
2497 					{
2498 
2499 						// ~~~~~~~~~~ SOUL SCREAM
2500 
2501 						if (chargeLevelAttained == 1)
2502 						{
2503 							if (dsq->continuity.dualFormCharge >= requiredDualFormCharge)
2504 							{
2505 								core->sound->playSfx("DualForm-Scream");
2506 
2507 								if (core->afterEffectManager)
2508 									core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
2509 
2510 								dsq->continuity.dualFormCharge = 0;
2511 								dsq->shakeCamera(25, 2);
2512 
2513 								core->globalScale = Vector(0.4, 0.4);
2514 								core->globalScaleChanged();
2515 								myZoom = Vector(0.4, 0.4);
2516 
2517 								/*
2518 								setv(EV_NOINPUTNOVEL, 0);
2519 								core->globalScale = Vector(1.5, 1.5);
2520 								core->main(0.5);
2521 								setv(EV_NOINPUTNOVEL, 1);
2522 								*/
2523 
2524 
2525 								FOR_ENTITIES(i)
2526 								{
2527 									Entity *e = *i;
2528 									if (e->getEntityType() == ET_ENEMY && e != this)
2529 									{
2530 										if (e->isv(EV_SOULSCREAMRADIUS, -1) || (e->position - position).isLength2DIn(1000 + e->getv(EV_SOULSCREAMRADIUS)))
2531 										{
2532 											DamageData d;
2533 											d.damage = 20;
2534 											d.damageType = DT_AVATAR_DUALFORMNAIJA;
2535 											d.attacker = this;
2536 											d.form = dsq->continuity.form;
2537 											e->damage(d);
2538 										}
2539 									}
2540 								}
2541 
2542 								/*
2543 								setv(EV_NOINPUTNOVEL, 0);
2544 								core->main(0.5);
2545 								dsq->screenTransition->capture();
2546 								dsq->screenTransition->go(0.5);
2547 								myZoom = Vector(1,1);
2548 								setv(EV_NOINPUTNOVEL, 1);
2549 								*/
2550 							}
2551 							else
2552 							{
2553 								core->sound->playSfx("Denied");
2554 							}
2555 						}
2556 					}
2557 					else if (dsq->continuity.dualFormMode == Continuity::DUALFORM_LI)
2558 					{
2559 						if (chargeLevelAttained == 1)
2560 						{
2561 							int i = 0;
2562 							int num = 5;
2563 							for (; i < num; i++)
2564 							{
2565 								Shot *s = dsq->game->fireShot("DualForm", this, 0, position, 0);
2566 								//*0.5f + getAim()*0.5f
2567 								Vector v1 = this->getTendrilAimVector(i, num);
2568 								Vector v2 = getAim();
2569 								v1.normalize2D();
2570 								v2.normalize2D();
2571 								s->setAimVector(v1*0.1f + v2*0.9f);
2572 							}
2573 							core->sound->playSfx("DualForm-Shot");
2574 							dsq->spawnParticleEffect("DualFormFire", position);
2575 
2576 							/*
2577 							didShockDamage = false;
2578 							doShock("DualFormLiTendril");
2579 							*/
2580 						}
2581 						else
2582 						{
2583 							core->sound->playSfx("Denied");
2584 							/*
2585 							if (!fireDelay)
2586 							{
2587 								if (fireAtNearestValidEntity("DualFormLi"))
2588 								{
2589 									fireDelay = fireDelayTime;
2590 								}
2591 							}
2592 							*/
2593 						}
2594 					}
2595 				}
2596 			}
2597 			/*
2598 			else if (ability == 1)
2599 			{
2600 
2601 			}
2602 			*/
2603 		}
2604 	break;
2605 	case FORM_ENERGY:
2606 		{
2607 			if (ability == 0)
2608 			{
2609 				if (chargeLevelAttained == 2)
2610 				{
2611 					if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2612 						doShock("EnergyTendril2");
2613 					else
2614 						doShock("EnergyTendril");
2615 					if (!state.lockedToWall)
2616 						skeletalSprite.animate("energyChargeAttack", 0, ANIMLAYER_UPPERBODYIDLE);
2617 
2618 					/*
2619 					if (core->afterEffectManager)
2620 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter, 0.1,0.03,30,0.2f, 1.5));
2621 					*/
2622 					dsq->playVisualEffect(VFX_SHOCK, position, this);
2623 				}
2624 				else
2625 				{
2626 					if (!fireDelay)
2627 					{
2628 						std::string shotName;
2629 						if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2630 							shotName = "EnergyBlast2";
2631 						else
2632 							shotName = "EnergyBlast";
2633 
2634 						if (fireAtNearestValidEntity(shotName))
2635 						{
2636 							fireDelay = fireDelayTime;
2637 						}
2638 					}
2639 				}
2640 			}
2641 		}
2642 		break;
2643 	case FORM_NATURE:
2644 		// no abilities
2645 		{
2646 			//debugLog("rock ability");
2647 			if (ability == 0)
2648 			{
2649 				if (formAbilityDelay == 0)
2650 				{
2651 					formAbilityDelay = 0.2;
2652 					//Vector pos = dsq->getGameCursorPosition() - position;
2653 
2654 					Vector pos = getAim();
2655 					if (!pos.isZero())
2656 						pos.setLength2D(16);
2657 					pos += position;
2658 
2659 					std::string seedName;
2660 					if (chargeLevelAttained == 0)
2661 						seedName = "SeedFlower";
2662 					else if (chargeLevelAttained == 2)
2663 						seedName = "SeedUberVine";
2664 
2665 					dsq->game->fireShot(seedName, this, 0, pos, getAim());
2666 
2667 
2668 					/*
2669 					Vector pos = getAim();
2670 					if (!pos.isZero())
2671 						pos.setLength2D(64);
2672 					pos += position;
2673 
2674 
2675 					//dsq->spawnParticleEffect("Fertilizer", pos);
2676 
2677 					Entity *e = 0;
2678 					std::string seedName;
2679 					if (chargeLevelAttained == 0)
2680 						seedName = "SeedFlower";
2681 					else if (chargeLevelAttained == 2)
2682 						seedName = "SeedUberVine";
2683 
2684 					e = dsq->game->createEntity(seedName, 0, pos, 0, false, "");
2685 
2686 					Vector add = pos - position;
2687 					add.setLength2D(800);
2688 					e->vel += add;
2689 					*/
2690 
2691 					/*
2692 					if (chargeLevelAttained == 0)
2693 					{
2694 					}
2695 					else if (chargeLevelAttained == 1)
2696 					{
2697 						e->setState(STATE_CHARGE1);
2698 					}
2699 					else if (chargeLevelAttained == 2)
2700 					{
2701 						e->setState(STATE_CHARGE2);
2702 					}
2703 
2704 					e->update(0);
2705 					*/
2706 					// idle = charge 0
2707 					// attack = charge1
2708 					// something = charge2
2709 					/*
2710 					FOR_ENTITIES (i)
2711 					{
2712 						Entity *e = *i;
2713 						if (e && e->getEntityType() == ET_ENEMY && e->isDamageTarget(DT_AVATAR_NATURE))
2714 						{
2715 							if ((e->position - pos).isLength2DIn(128))
2716 							{
2717 								DamageData d;
2718 								d.damageType = DT_AVATAR_NATURE;
2719 								d.damage = 1;
2720 								d.attacker = this;
2721 								e->damage(d);
2722 							}
2723 						}
2724 					}
2725 					*/
2726 				}
2727 			}
2728 		}
2729 	break;
2730 	case FORM_BEAST:
2731 		{
2732 
2733 			if (!dsq->continuity.isNaijaEatsEmpty())
2734 			{
2735 				EatData *d = dsq->continuity.getLastNaijaEat();
2736 				if (!d->shot.empty())
2737 				{
2738 					int num = getNumShots()-2;
2739 
2740 					for (int i = 0; i < num; i++)
2741 					{
2742 						bool playSfx = true;
2743 						if (i > 0)
2744 							playSfx = false;
2745 						Shot *s = dsq->game->fireShot(d->shot, this, 0, Vector(0,0,0), Vector(0,0,0), playSfx);
2746 						if (s->shotData && s->shotData->damage > 0)
2747 						{
2748 							s->extraDamage = 1;
2749 						}
2750 
2751 						Entity *target = 0;
2752 						if (s->shotData->homing > 0)
2753 						{
2754 							Vector p = dsq->getGameCursorPosition();
2755 							target = dsq->game->getNearestEntity(p, 800, this, ET_ENEMY, s->getDamageType());
2756 						}
2757 						if (target)
2758 						{
2759 							s->target = target;
2760 						}
2761 
2762 						if (bone_head)
2763 						{
2764 							s->position = bone_head->getWorldPosition();
2765 						}
2766 						else
2767 						{
2768 							s->position = this->position;
2769 						}
2770 
2771 						Vector aim = getVectorToCursor();
2772 						if (aim.isZero())
2773 							aim = getForwardAim();
2774 						if (num == 1)
2775 							s->setAimVector(aim);
2776 						else
2777 						{
2778 							aim.normalize2D();
2779 							s->setAimVector(getTendrilAimVector(i, num)*0.1f + aim*0.9f);
2780 						}
2781 
2782 						if (s->shotData && s->shotData->avatarKickBack)
2783 						{
2784 							Vector d = s->velocity;
2785 							d.setLength2D(-s->shotData->avatarKickBack);
2786 							float effect = 1;
2787 							if (!isUnderWater())
2788 								effect = 0.4;
2789 							push(d, s->shotData->avatarKickBackTime * effect, s->shotData->avatarKickBack * effect, 0);
2790 						}
2791 					}
2792 
2793 					debugLog("firing: " + d->shot);
2794 					d->ammo--;
2795 				}
2796 				if (d->ammo <= 0)
2797 					dsq->continuity.removeLastNaijaEat();
2798 					//dsq->continuity.removeEatData(eats.size()-1);
2799 			}
2800 
2801 			/*
2802 			switch(inTummy)
2803 			{
2804 			case EAT_BASICSHOT:
2805 			{
2806 				tummyAmount --;
2807 				// FIRE!
2808 				if (tummyAmount <= 0)
2809 				{
2810 
2811 					//fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000);
2812 					inTummy = EAT_NONE;
2813 				}
2814 			}
2815 			break;
2816 			}
2817 			*/
2818 			/*
2819 			if (inTummy > 0)
2820 			{
2821 				if (inTummy > 3)
2822 					inTummy = 3;
2823 				if (fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000))
2824 				{
2825 					inTummy = 0;
2826 				}
2827 			}
2828 			*/
2829 			/*
2830 			if (ability == 0)
2831 			{
2832 				Vector bitePos = position;
2833 				Vector offset = vel;
2834 				offset.setLength2D(128);
2835 				bitePos += offset;
2836 
2837 				FOR_ENTITIES (i)
2838 				{
2839 					Entity *e = *i;
2840 					if (e && (e->position - bitePos).getSquaredLength2D() < sqr(64))
2841 					{
2842 						DamageData d;
2843 						d.attacker = this;
2844 						d.damage = 2;
2845 						e->damage(d);
2846 						heal(1);
2847 					}
2848 				}
2849 			}
2850 			else
2851 			{
2852 			}
2853 			*/
2854 		}
2855 	break;
2856 	case FORM_SUN:
2857 	{
2858 		if (formAbilityDelay == 0 && chargeLevelAttained==1)
2859 		{
2860 			core->sound->playSfx("SunForm");
2861 			//dsq->spawnParticleEffect("LightFlare", position);
2862 
2863 			chargeEmitter->load("SunFlare");
2864 			chargeEmitter->start();
2865 
2866 			PauseQuad *q = new PauseQuad;
2867 			q->setTexture("Naija/LightFormGlow");
2868 			q->position = position;
2869 			q->setWidthHeight(1024, 1024);
2870 			q->setLife(1);
2871 			q->setDecayRate(0.05);
2872 			q->fadeAlphaWithLife = 1;
2873 			q->scale = Vector(0,0);
2874 			q->scale.interpolateTo(Vector(2,2), 0.1);
2875 			dsq->game->addRenderObject(q, LR_ELEMENTS13);
2876 			q->moveToFront();
2877 
2878 			FOR_ENTITIES(i)
2879 			{
2880 				Entity *e = *i;
2881 				if (e != this && (e->position - position).isLength2DIn(2048))
2882 				{
2883 					e->lightFlare();
2884 				}
2885 			}
2886 			//formAbilityDelay = 0.1;
2887 		}
2888 	}
2889 	break;
2890 	case FORM_SPIRIT:
2891 		// spirit beacon
2892 		// absorbs nearby shots, and respawns the player if in a "SPIRITBEACON" node
2893 		if (formAbilityDelay == 0)
2894 		{
2895 			core->sound->playSfx("Spirit-Beacon");
2896 			//dsq->spawnParticleEffect("SpiritBeacon", position);
2897 			std::list<Shot*> delShots;
2898 			Shot::Shots::iterator i;
2899 			for (i = Shot::shots.begin(); i != Shot::shots.end(); i++)
2900 			{
2901 				Shot *s = (*i);
2902 				if (s->isActive() && s->shotData && !s->shotData->invisible)
2903 				{
2904 					if (!s->firer || s->firer->getEntityType()==ET_ENEMY)
2905 					{
2906 						if ((s->position - position).isLength2DIn(256))
2907 						{
2908 							//s->safeKill();
2909 							delShots.push_back(s);
2910 							spiritEnergyAbsorbed++;
2911 						}
2912 					}
2913 				}
2914 			}
2915 			for (std::list<Shot*>::iterator j = delShots.begin(); j != delShots.end(); j++)
2916 			{
2917 				Shot *s = (*j);
2918 				s->safeKill();
2919 			}
2920 			if (spiritEnergyAbsorbed > 4)
2921 			{
2922 				dsq->game->spawnManaBall(position, 1);
2923 				spiritEnergyAbsorbed = 0;
2924 			}
2925 			spiritBeaconEmitter.start();
2926 			formAbilityDelay = 1.0;
2927 
2928 			Path *p = dsq->game->getNearestPath(position, "SPIRITBEACON");
2929 			if (p && p->isCoordinateInside(position))
2930 			{
2931 				bodyPosition = position;
2932 				if (pullTarget)
2933 				{
2934 					pullTarget->position = position;
2935 				}
2936 				revert();
2937 			}
2938 			else
2939 			{
2940 				Path *p = dsq->game->getNearestPath(position, PATH_SPIRITPORTAL);
2941 				if (p && p->isCoordinateInside(position))
2942 				{
2943 					if (inSpiritWorld)
2944 						changeForm(FORM_NORMAL);
2945 					dsq->game->warpToSceneFromNode(p);
2946 				}
2947 			}
2948 		}
2949 	break;
2950 	case FORM_FISH:
2951 	{
2952 
2953 	}
2954 	break;
2955 	}
2956 }
2957 
getTendrilAimVector(int i,int max)2958 Vector Avatar::getTendrilAimVector(int i, int max)
2959 {
2960 	float a = float(float(i)/float(max))*PI*2;
2961 	Vector aim(sinf(a), cosf(a));
2962 	if (state.lockedToWall)
2963 	{
2964 		Vector n = dsq->game->getWallNormal(position);
2965 		if (!n.isZero())
2966 		{
2967 			aim = aim*0.4f + n*0.6f;
2968 		}
2969 	}
2970 	return aim;
2971 }
2972 
getNumShots()2973 int Avatar::getNumShots()
2974 {
2975 	int thits = normalTendrilHits;
2976 	if (flourishPowerTimer.isActive())
2977 	{
2978 		if (lastBurstType == BURST_WALL)
2979 			thits = maxTendrilHits;
2980 		else
2981 			thits = rollTendrilHits;
2982 	}
2983 	else
2984 	{
2985 		if (bursting)
2986 		{
2987 			if (lastBurstType == BURST_WALL)
2988 				thits = rollTendrilHits;
2989 		}
2990 	}
2991 	return thits;
2992 }
2993 
doShock(const std::string & shotName)2994 void Avatar::doShock(const std::string &shotName)
2995 {
2996 
2997 
2998 
2999 	int c = 0;
3000 	//int maxHit = 2 + dsq->continuity.getSpellLevel(SPELL_SHOCK)*2;
3001 	//int maxHit = 4;
3002 	std::vector <Entity*> entitiesToHit;
3003 	std::vector <Target> localTargets;
3004 	bool clearTargets = true;
3005 
3006 	int thits = getNumShots();
3007 
3008 	/*
3009 	if (skeletalSprite.getAnimationLayer(ANIMLAYER_FLOURISH)->getCurrentAnimation())
3010 	{
3011 		thits = maxTendrilHits;
3012 	}
3013 	*/
3014 
3015 	if (!targets.empty() && targets[0].e != 0)
3016 	{
3017 		clearTargets = false;
3018 		for (int i = 0; i < thits; i++)
3019 		{
3020 			entitiesToHit.push_back(targets[0].e);
3021 		}
3022 	}
3023 	else
3024 	{
3025 		//std::vector <Target> localTargets;
3026 
3027 		localTargets.clear();
3028 
3029 		int range = 800;
3030 		EntityList entityList;
3031 		FOR_ENTITIES(i)
3032 		{
3033 			Entity *e = *i;
3034 			if (e != this && e->isPresent() && e->isDamageTarget(DT_AVATAR_SHOCK) && (e->position - position).isLength2DIn(range))
3035 			{
3036 				entityList.push_back(e);
3037 			}
3038 		}
3039 
3040 		while (c < thits)
3041 		{
3042 			Target t = getNearestTarget(position, position, this, DT_AVATAR_SHOCK, true, &localTargets, &entityList);
3043 			if (t.e)
3044 			{
3045 				localTargets.push_back(t);
3046 				entitiesToHit.push_back(t.e);
3047 				targets.push_back(t);
3048 				c ++;
3049 			}
3050 			else
3051 			{
3052 				break;
3053 			}
3054 		}
3055 
3056 		if (!localTargets.empty())
3057 		{
3058 			while (entitiesToHit.size()<thits)
3059 			{
3060 				for (int i = 0; i < localTargets.size(); i++)
3061 				{
3062 					if (!(entitiesToHit.size()<thits))
3063 						break;
3064 					entitiesToHit.push_back(localTargets[i].e);
3065 					targets.push_back(localTargets[i]);
3066 				}
3067 			}
3068 		}
3069 		localTargets.clear();
3070 	}
3071 
3072 	Vector aim = getAim();
3073 	aim.normalize2D();
3074 	int sz = entitiesToHit.size();
3075 
3076 
3077 
3078 
3079 	if (sz == 0)
3080 	{
3081 		for (int i = 0; i < thits; i++)
3082 		{
3083 			Shot *s = dsq->game->fireShot(shotName, this, 0);
3084 
3085 			s->setAimVector(getTendrilAimVector(i, thits));
3086 
3087 			checkUpgradeForShot(s);
3088 		}
3089 	}
3090 	else
3091 	{
3092 		for (int i = 0; i < sz; i++)
3093 		{
3094 			Entity *e = entitiesToHit[i];
3095 			if (e)
3096 			{
3097 				Shot *s = dsq->game->fireShot(shotName, this, e);
3098 				if (!targets.empty())
3099 				{
3100 					for (int j = 0; j < targets.size(); j++)
3101 					{
3102 						if (targets[j].e == e)
3103 							s->targetPt = targets[j].targetPt;
3104 					}
3105 				}
3106 				/*
3107 				else if (!localTargets.empty())
3108 				{
3109 					for (int j = 0; j < localTargets.size(); j++)
3110 					{
3111 						if (localTargets[j].e == e)
3112 							s->targetPt = localTargets[j].targetPt;
3113 					}
3114 				}
3115 				*/
3116 				Vector d = e->position - position;
3117 				/*
3118 				float a = float(float(i)/float(sz))*PI*2;
3119 				Vector aim(sinf(a), cosf(a));
3120 
3121 				swizzleTendrilAimVector(aim);
3122 				*/
3123 				s->setAimVector(getTendrilAimVector(i, thits));
3124 				checkUpgradeForShot(s);
3125 				/*
3126 				float ang = 0;
3127 				MathFunctions::calculateAngleBetweenVectorsInRadians(Vector(0,-1), d, ang);
3128 				float a = i-(sz/2);
3129 				Vector adjust = a*spread + ang;
3130 				d.normalize2D();
3131 				s->setAimVector((d + adjust)/2);
3132 				*/
3133 			}
3134 		}
3135 	}
3136 
3137 	if (clearTargets)
3138 	{
3139 		targets.clear();
3140 	}
3141 }
3142 
formAbilityUpdate(float dt)3143 void Avatar::formAbilityUpdate(float dt)
3144 {
3145 	switch(dsq->continuity.form)
3146 	{
3147 	case FORM_FISH:
3148 	{
3149 		if (core->mouse.buttons.right)
3150 		{
3151 			const float bubbleRate = 0.2;
3152 
3153 			state.abilityDelay -= dt;
3154 			if (state.abilityDelay < 0)
3155 				state.abilityDelay = 0;
3156 
3157 			if (state.abilityDelay == 0)
3158 			{
3159 				state.abilityDelay = bubbleRate;
3160 				//state.abilityDelay -= bubbleRate;
3161 				// spawn bubble
3162 				//Entity *bubble = dsq->game->createEntity("FishFormBubble", 0, position, 0, false, "");
3163 				Vector dir = getAim();
3164 				dir.normalize2D();
3165 
3166 				dsq->game->fireShot("FishFormBubble", this, 0, position+dir*16, dir);
3167 			}
3168 		}
3169 	}
3170 	break;
3171 	case FORM_ENERGY:
3172 	{
3173 	}
3174 	break;
3175 	}
3176 }
3177 
isMouseInputEnabled()3178 bool Avatar::isMouseInputEnabled()
3179 {
3180 	if (!inputEnabled) return false;
3181 	//if (dsq->continuity.getWorldType() != WT_NORMAL) return false;
3182 	//if (getState() != STATE_IDLE) return false;
3183 	if (dsq->game->isPaused()) return false;
3184 	return true;
3185 }
3186 
rmbd()3187 void Avatar::rmbd()
3188 {
3189 	//core->setDockIcon("BitBlot");
3190 	if (!isMouseInputEnabled() || isEntityDead()) return;
3191 	if (dsq->continuity.form == FORM_NORMAL )
3192 	{
3193 		//if (isCoordinateInRadius(dsq->getGameCursorPosition(), 96))
3194 		///Vector diff = core->mouse.position - c;
3195 		if (dsq->inputMode == INPUT_MOUSE)
3196 		{
3197 			Vector diff = getVectorToCursorFromScreenCentre();
3198 			if (diff.getSquaredLength2D() < sqr(openSingingInterfaceRadius))
3199 				openSingingInterface();
3200 		}
3201 		else
3202 		{
3203 			openSingingInterface();
3204 		}
3205 	}
3206 	else
3207 	{
3208 		startCharge(0);
3209 	}
3210 }
3211 
rmbu()3212 void Avatar::rmbu()
3213 {
3214 	if (!isMouseInputEnabled() || isEntityDead()) return;
3215 
3216 	if (charging)
3217 	{
3218 		if (!entityToActivate && !pathToActivate)
3219 			formAbility(0);
3220 
3221 		endCharge();
3222 	}
3223 
3224 
3225 	dsq->cursorGlow->alpha.interpolateTo(0, 0.2);
3226 	dsq->cursorBlinker->alpha.interpolateTo(0, 0.2);
3227 
3228 	if (singing)
3229 	{
3230 		closeSingingInterface();
3231 	}
3232 
3233 
3234 	if (entityToActivate)
3235 	{
3236 		activateEntity = entityToActivate;
3237 		entityToActivate = 0;
3238 	}
3239 	if (pathToActivate)
3240 	{
3241 		pathToActivate->activate();
3242 		pathToActivate = 0;
3243 	}
3244 }
3245 
canCharge(int ability)3246 bool Avatar::canCharge(int ability)
3247 {
3248 	switch(dsq->continuity.form)
3249 	{
3250 	case FORM_ENERGY:
3251 		if (ability == 0) return true;
3252 	break;
3253 	case FORM_BEAST:
3254 		return false;
3255 	break;
3256 	case FORM_DUAL:
3257 		return true;
3258 	break;
3259 	case FORM_NATURE:
3260 		if (ability == 0)
3261 			return true;
3262 	break;
3263 	case FORM_SUN:
3264 		return true;
3265 	break;
3266 	}
3267 	return false;
3268 }
3269 
startCharge(int ability)3270 void Avatar::startCharge(int ability)
3271 {
3272 	if (!isCharging() && canCharge(ability))
3273 	{
3274 		if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
3275 		{
3276 			core->sound->stopSfx(dsq->loops.charge);
3277 			dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
3278 		}
3279 
3280 		PlaySfx sfx;
3281 		sfx.name = "ChargeLoop";
3282 		sfx.loops = -1;
3283 		dsq->loops.charge = core->sound->playSfx(sfx);
3284 
3285 		state.spellCharge = 0;
3286 		chargeLevelAttained = 0;
3287 
3288 		switch(dsq->continuity.form)
3289 		{
3290 		case FORM_ENERGY:
3291 			chargingEmitter->load("ChargingEnergy");
3292 		break;
3293 		case FORM_NATURE:
3294 			chargingEmitter->load("ChargingNature");
3295 		break;
3296 		case FORM_SUN:
3297 			chargingEmitter->load("ChargingEnergy");
3298 		break;
3299 		case FORM_DUAL:
3300 			chargingEmitter->load("ChargingDualForm");
3301 		break;
3302 		default:
3303 			chargingEmitter->load("ChargingGeneric");
3304 		break;
3305 		}
3306 
3307 		chargingEmitter->start();
3308 
3309 		charging = true;
3310 
3311 	}
3312 	if (!canCharge(ability))
3313 	{
3314 		formAbility(ability);
3315 	}
3316 }
3317 
setBlockSinging(bool v)3318 void Avatar::setBlockSinging(bool v)
3319 {
3320 	blockSinging = v;
3321 	if (v)
3322 	{
3323 		currentSong.notes.clear(); // abort singing without triggering a song, if queued
3324 		closeSingingInterface();
3325 	}
3326 }
3327 
canSetBoneLock()3328 bool Avatar::canSetBoneLock()
3329 {
3330 	return true;
3331 }
3332 
onSetBoneLock()3333 void Avatar::onSetBoneLock()
3334 {
3335 	Entity::onSetBoneLock();
3336 
3337 	if (boneLock.on)
3338 	{
3339 		skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
3340 		lockToWallCommon();
3341 		state.lockedToWall = 1;
3342 		wallNormal = boneLock.localOffset;
3343 		wallNormal.normalize2D();
3344 		rotateToVec(wallNormal, 0.1);
3345 	}
3346 	else
3347 	{
3348 		if (state.lockedToWall)
3349 		{
3350 			fallOffWall();
3351 		}
3352 	}
3353 }
3354 
onUpdateBoneLock()3355 void Avatar::onUpdateBoneLock()
3356 {
3357 	Entity::onUpdateBoneLock();
3358 
3359 	wallNormal = boneLock.wallNormal;
3360 	rotateToVec(wallNormal, 0.01);
3361 }
3362 
lmbd()3363 void Avatar::lmbd()
3364 {
3365 	if (!isMouseInputEnabled()) return;
3366 
3367 	// getstopdistance
3368 	if (_isUnderWater)
3369 	{
3370 		Vector v = getVectorToCursor();
3371 		if (v.isLength2DIn(getStopDistance()) && !v.isLength2DIn(minMouse))
3372 		{
3373 			if (state.lockedToWall)
3374 			{
3375 				fallOffWall();
3376 			}
3377 		}
3378 	}
3379 }
3380 
fallOffWall()3381 void Avatar::fallOffWall()
3382 {
3383 	//stillTimer.stop();
3384 	if (state.lockedToWall)
3385 	{
3386 		lockToWallFallTimer = 0;
3387 		state.nearWall = false;
3388 		state.lockedToWall = false;
3389 
3390 		setBoneLock(BoneLock());
3391 
3392 
3393 		idle();
3394 		offset.interpolateTo(Vector(0,0), 0.1);
3395 		if (!wallNormal.isZero())
3396 		{
3397 			Vector velSet = wallNormal;
3398 			velSet.setLength2D(200);
3399 			vel += velSet;
3400 		}
3401 		//doCollisionAvoidance(dt, 5, 1);
3402 	}
3403 }
3404 
lmbu()3405 void Avatar::lmbu()
3406 {
3407 	if (!isMouseInputEnabled()) return;
3408 
3409 	if (dsq->continuity.toggleMoveMode)
3410 		movingOn = !movingOn;
3411 
3412 	if (isSinging())
3413 	{
3414 		// switch menu
3415 	}
3416 }
3417 
isCharging()3418 bool Avatar::isCharging()
3419 {
3420 	return charging;
3421 }
3422 
endCharge()3423 void Avatar::endCharge()
3424 {
3425 	if (charging)
3426 	{
3427 		if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
3428 		{
3429 			core->sound->stopSfx(dsq->loops.charge);
3430 			dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
3431 		}
3432 
3433 		chargingEmitter->stop();
3434 
3435 		charging = false;
3436 		state.spellCharge = 0;
3437 	}
3438 }
3439 
getWallNormal(TileVector t)3440 Vector Avatar::getWallNormal(TileVector t)
3441 {
3442 	return dsq->game->getWallNormal(t.worldVector(), 5)*-1;
3443 }
3444 
getSingingInterfaceRadius()3445 int Avatar::getSingingInterfaceRadius()
3446 {
3447 	return singingInterfaceRadius;
3448 }
3449 
getOpenSingingInterfaceRadius()3450 int Avatar::getOpenSingingInterfaceRadius()
3451 {
3452 	return openSingingInterfaceRadius;
3453 }
3454 
isSwimming()3455 bool Avatar::isSwimming()
3456 {
3457 	return swimming;
3458 }
3459 
lockToWallCommon()3460 void Avatar::lockToWallCommon()
3461 {
3462 	swimEmitter.stop();
3463 
3464 	skeletalSprite.stopAllAnimations();
3465 	rotationOffset.interpolateTo(0, 0.01);
3466 
3467 	fallGravityTimer = 0;
3468 
3469 	dsq->spawnParticleEffect("LockToWall", position);
3470 	stopBurst();
3471 	stopRoll();
3472 	core->sound->playSfx("LockToWall", 1.0);
3473 	//bursting = false;
3474 	this->burst = 1;
3475 	//lastLockToWallPos = position;
3476 
3477 	state.lockToWallDelay.start(0.2);
3478 	state.lockedToWall = true;
3479 
3480 	lockToWallFallTimer = -1;
3481 
3482 	// move this to its own function?
3483 	state.backFlip = false;
3484 	skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
3485 }
3486 
lockToWall()3487 void Avatar::lockToWall()
3488 {
3489 	if (riding) return;
3490 	if (inCurrent && !canSwimAgainstCurrents()) return;
3491 	if (!canLockToWall()) return;
3492 	if (state.lockedToWall) return;
3493 	if (vel.x == 0 && vel.y == 0) return;
3494 	if (dsq->game->isPaused()) return;
3495 
3496 	TileVector t(position);
3497 	Vector m = vel;
3498 	m.setLength2D(3);
3499 	t.x += int(m.x);
3500 	t.y += int(m.y);
3501 
3502 	bool good = true;
3503 	if (!dsq->game->isObstructed(t))
3504 	{
3505 		do
3506 		{
3507 			TileVector test;
3508 
3509 			test = TileVector(t.x, t.y+1);
3510 			if (dsq->game->isObstructed(test))
3511 			{
3512 				t = test;
3513 				break;
3514 			}
3515 			test = TileVector(t.x, t.y-1);
3516 			if (dsq->game->isObstructed(test))
3517 			{
3518 				t = test;
3519 				break;
3520 			}
3521 			test = TileVector(t.x-1, t.y);
3522 			if (dsq->game->isObstructed(test))
3523 			{
3524 				t = test;
3525 				break;
3526 			}
3527 			test = TileVector(t.x+1, t.y);
3528 			if (dsq->game->isObstructed(test))
3529 			{
3530 				t = test;
3531 				break;
3532 			}
3533 			test = TileVector(t.x+1, t.y+1);
3534 			if (dsq->game->isObstructed(test))
3535 			{
3536 				t = test;
3537 				break;
3538 			}
3539 			test = TileVector(t.x-1, t.y+1);
3540 			if (dsq->game->isObstructed(test))
3541 			{
3542 				t = test;
3543 				break;
3544 			}
3545 			test = TileVector(t.x+1, t.y-1);
3546 			if (dsq->game->isObstructed(test))
3547 			{
3548 				t = test;
3549 				break;
3550 			}
3551 			test = TileVector(t.x-1, t.y-1);
3552 			if (dsq->game->isObstructed(test))
3553 			{
3554 				t = test;
3555 				break;
3556 			}
3557 
3558 			good = false;
3559 		}
3560 		while(0);
3561 	}
3562 
3563 	if (dsq->game->isObstructed(t, OT_HURT) && isDamageTarget(DT_WALLHURT))
3564 	{
3565 		good = false;
3566 	}
3567 	if (good)
3568 	{
3569 		wallNormal = dsq->game->getWallNormal(position);
3570 		bool outOfWaterHit = (!_isUnderWater && !(wallNormal.y < -0.1f));
3571 		if (wallNormal.isZero() )
3572 		{
3573 			debugLog("COULD NOT FIND NORMAL, GOING TO BOUNCE");
3574 			return;
3575 		}
3576 		else
3577 		{
3578 			if (!dsq->mod.isActive() && !dsq->continuity.getFlag("lockedToWall"))
3579 			{
3580 
3581 				if (!dsq->game->isControlHint()){
3582 					dsq->continuity.setFlag("lockedToWall", 1);
3583 					dsq->game->setControlHint(dsq->continuity.stringBank.get(13), 1, 0, 0, 6, "", true);
3584 				}
3585 			}
3586 
3587 			lockToWallCommon();
3588 
3589 			if (outOfWaterHit)
3590 				lockToWallFallTimer = 0.4;
3591 			else
3592 				lockToWallFallTimer = -1;
3593 
3594 			wallPushVec = wallNormal;
3595 			wallPushVec *= 2000;
3596 			wallPushVec.z = 0;
3597 			skeletalSprite.stopAllAnimations();
3598 			if (wallPushVec.y < 0 && (fabsf(wallPushVec.y) > fabsf(wallPushVec.x)))
3599 			{
3600 				skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
3601 			}
3602 			else
3603 			{
3604 				skeletalSprite.transitionAnimate("wall", 0.2, -1);
3605 			}
3606 			rotateToVec(wallPushVec, 0.1);
3607 
3608 			offset.stop();
3609 
3610 			int tileType = dsq->game->getGrid(t);
3611 			Vector offdiff = t.worldVector() - position;
3612 			if (!offdiff.isZero())
3613 			{
3614 				if (tileType & OT_INVISIBLEIN)
3615 				{
3616 					Vector adjust = offdiff;
3617 					adjust.setLength2D(TILE_SIZE/2);
3618 					offdiff -= adjust;
3619 				}
3620 				else
3621 				{
3622 					Vector adjust = offdiff;
3623 					adjust.setLength2D(TILE_SIZE*2);
3624 					offdiff -= adjust;
3625 				}
3626 			}
3627 
3628 			float spd = vel.getLength2D();
3629 			if (spd < 1000)
3630 				spd = 1000;
3631 
3632 			Vector diff = offset - offdiff;
3633 			float len = diff.getLength2D();
3634 			float time = 0;
3635 			if (len > 0)
3636 			{
3637 				time = len/spd;
3638 			}
3639 			offset.interpolateTo(offdiff, time);
3640 
3641 			wallLockTile = t;
3642 
3643 			vel = Vector(0,0,0);
3644 			vel2 = 0;
3645 		}
3646 	}
3647 	else
3648 	{
3649 		//debugLog("COULD NOT FIND TILE TO GRAB ONTO");
3650 		//position = opos;
3651 	}
3652 }
3653 
applyTripEffects()3654 void Avatar::applyTripEffects()
3655 {
3656 	color.interpolateTo(BLIND_COLOR, 0.5);
3657 
3658 	tripper->alpha.interpolateTo(1, 8);
3659 
3660 	tripper->color = Vector(1, 1, 1);
3661 	tripper->rotation.z = 0;
3662 	tripper->rotation.interpolateTo(Vector(0, 0, 360), 10, -1);
3663 	tripper->scale = Vector(1.25, 1.25, 1.25);
3664 	tripper->scale.interpolateTo(Vector(1.3, 1.3, 1.3), 2, -1, 1, 1);
3665 
3666 	if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
3667 	{
3668 		dsq->sound->stopSfx(dsq->loops.trip);
3669 		dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
3670 	}
3671 
3672 	PlaySfx play;
3673 	play.name = "TripLoop";
3674 	play.loops = -1;
3675 	play.fade = SFT_IN;
3676 	play.time = 1;
3677 	dsq->loops.trip = dsq->sound->playSfx(play);
3678 }
3679 
removeTripEffects()3680 void Avatar::removeTripEffects()
3681 {
3682 	color.interpolateTo(Vector(1,1,1),0.5);
3683 	tripper->alpha.interpolateTo(0, 4);
3684 
3685 	if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
3686 	{
3687 		dsq->sound->fadeSfx(dsq->loops.trip, SFT_OUT, 3);
3688 		dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
3689 	}
3690 }
3691 
applyBlindEffects()3692 void Avatar::applyBlindEffects()
3693 {
3694 	// screen black
3695 
3696 	// character black
3697 	color.interpolateTo(BLIND_COLOR, 0.5);
3698 	blinder->alpha.interpolateTo(1, 0.5);
3699 
3700 	blinder->rotation.z = 0;
3701 	blinder->rotation.interpolateTo(Vector(0, 0, 360), 10, -1);
3702 	blinder->scale = Vector(1.25, 1.25, 1.25);
3703 	blinder->scale.interpolateTo(Vector(1.3, 1.3, 1.3), 2, -1, 1, 1);
3704 
3705 	//dsq->toggleMuffleSound(1);
3706 }
3707 
removeBlindEffects()3708 void Avatar::removeBlindEffects()
3709 {
3710 	color.interpolateTo(Vector(1,1,1),0.5);
3711 	blinder->alpha.interpolateTo(0, 0.5);
3712 	//dsq->toggleMuffleSound(0);
3713 }
3714 
setBlind(float time)3715 void Avatar::setBlind(float time)
3716 {
3717 	if (time == 0)
3718 	{
3719 		removeBlindEffects();
3720 		return;
3721 	}
3722 
3723 	if (!state.blind)
3724 	{
3725 		applyBlindEffects();
3726 	}
3727 	state.blind = true;
3728 	if (time > state.blindTimer.getValue())
3729 		///*state.blindTimer.getValue() + */
3730 		state.blindTimer.start(time);
3731 }
3732 
setNearestPullTarget()3733 void Avatar::setNearestPullTarget()
3734 {
3735 	const float maxDistSqr = sqr(800);
3736 	float smallestDist = maxDistSqr;
3737 	Entity *closest = 0;
3738 	FOR_ENTITIES(i)
3739 	{
3740 		Entity *e = *i;
3741 		if (e)
3742 		{
3743 			if ((e->isPullable()) && e->life == 1)
3744 			{
3745 				float dist = (e->position - position).getSquaredLength2D();
3746 				if (dist < smallestDist)
3747 				{
3748 					closest = e;
3749 					smallestDist = dist;
3750 				}
3751 			}
3752 		}
3753 	}
3754 	if (closest)
3755 	{
3756 		pullTarget = closest;
3757 		pullTarget->startPull();
3758 	}
3759 }
3760 
createWeb()3761 void Avatar::createWeb()
3762 {
3763 	web = new Web;
3764 	web->setParentEntity(this);
3765 	dsq->game->addRenderObject(web, LR_ENTITIES);
3766 	curWebPoint = web->addPoint(dsq->game->avatar->position);
3767 	curWebPoint = web->addPoint(dsq->game->avatar->position);
3768 }
3769 
clearWeb()3770 void Avatar::clearWeb()
3771 {
3772 	if (web)
3773 	{
3774 		web->setExistence(25);
3775 		//web->setLife(1);
3776 		//web->setDecayRate(1.0f/30.0f);
3777 		//web->fadeAlphaWithLife = 1;
3778 
3779 		web = 0;
3780 	}
3781 }
3782 
Avatar()3783 Avatar::Avatar() : Entity(), ActionMapper()
3784 {
3785 	canDie = true;
3786 
3787 	urchinDelay = 0;
3788 	jellyDelay = 0;
3789 #ifdef AQ_TEST_QUADTRAIL
3790 	quadTrail = new QuadTrail(100, 32);
3791 	quadTrail->setTexture("Particles/QuadTrail");
3792 	quadTrail->setBlendType(BLEND_ADD);
3793 	dsq->game->addRenderObject(quadTrail, LR_PARTICLES);
3794 #endif
3795 
3796 	curWebPoint = 0;
3797 
3798 	web = 0;
3799 
3800 	lastBurstType = BURST_NONE;
3801 	dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
3802 	leftHandEmitter = rightHandEmitter = 0;
3803 	boneLeftHand = boneRightHand = 0;
3804 	canChangeForm = true;
3805 	biteTimer = 0;
3806 	dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
3807 	//heartbeat = 0;
3808 
3809 	lastNote = -1;
3810 	headTextureTimer = 0;
3811 	bone_dualFormGlow = 0;
3812 	//dsq->continuity.dualFormCharge = 0;
3813 	//dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
3814 	debugLog("Avatar 1");
3815 
3816 	//registerEntityDied = true;
3817 	setv(EV_ENTITYDIED, 1);
3818 	wallBurstTimer = 0;
3819 	beautyFlip = false;
3820 	invincibleBreak = true;
3821 	targetUpdateDelay = 0;
3822 	biteDelay = 0;
3823 	songInterfaceTimer = 0;
3824 	quickSongCastDelay = 0;
3825 	flourish = false;
3826 
3827 	blockSinging = false;
3828 	singing = false;
3829 
3830 	spiritEnergyAbsorbed = 0;
3831 	joystickMove = false;
3832 
3833 	debugLog("setCanLeaveWater");
3834 
3835 	setCanLeaveWater(true);
3836 
3837 	debugLog("setOverrideRenderPass");
3838 
3839 	setOverrideRenderPass(1);
3840 
3841 	debugLog("Done those");
3842 	/*
3843 	setRenderPass(2);
3844 	*/
3845 	rippleDelay = 0;
3846 	ripples = false;
3847 	fallGravityTimer = 0;
3848 	lastOutOfWaterMaxSpeed = 0;
3849 	//chargeGraphic = 0;
3850 	shieldPoints = auraTimer = 0;
3851 	glow = 0;
3852 
3853 	fireDelay = 0;
3854 	looking = false;
3855 	rollDidOne = 0;
3856 	lastQuad = lastQuadDir = rollDelay = rolling = 0;
3857 	chargeLevelAttained = 0;
3858 	activeAura = AURA_NONE;
3859 	movingOn = false;
3860 	currentMaxSpeed = 0;
3861 	pullTarget = 0;
3862 	revertTimer = 0;
3863 	currentSongIdx = -1;
3864 	leaches = 0;
3865 
3866 
3867 	debugLog("Avatar vars->");
3868 
3869 	damageTime = vars->avatarDamageTime;
3870 
3871 	activateEntity = 0;
3872 	canMove = true;
3873 	//scale = Vector(0.5, 0.5);
3874 	scale = Vector(0.5, 0.5);
3875 
3876 	debugLog("Avatar 2");
3877 	//scale = Vector(1.0, 1.0);
3878 	//setTexture("Naija-sprite2");
3879 	renderQuad = false;
3880 	name = "Naija";
3881 	setEntityType(ET_AVATAR);
3882 
3883 	targets.resize(1);
3884 
3885 	entityToActivate = 0;
3886 	pathToActivate = 0;
3887 	zoomOverriden = false;
3888 	canWarp = true;
3889 	blinder = 0;
3890 	zoomVel = 0;
3891 
3892 	myZoom = Vector(1,1);
3893 	this->pushingOffWallEffect = 0;
3894 	lockToWallFallTimer  = 0;
3895 	swimming = false;
3896 	charging = false;
3897 	bursting = false;
3898 	burst = 1;
3899 	burstDelay = 0;
3900 	splashDelay = 0;
3901 	avatar = this;
3902 
3903 	swimming = false;
3904 
3905 	debugLog("Avatar 3");
3906 	hair = new Hair();
3907 	hair->setTexture("Naija/Cape");
3908 	hair->setOverrideRenderPass(1);
3909 	hair->setRenderPass(1);
3910 	dsq->game->addRenderObject(hair, LR_ENTITIES);
3911 
3912 	debugLog("Avatar 4");
3913 
3914 	bindInput();
3915 
3916 	debugLog("Avatar 5");
3917 
3918 	blinder = new PauseQuad;
3919 	blinder->position = Vector(400, 300, 4.5);
3920 	blinder->setTexture("particles/blinder");
3921 	//blinder->width = blinder->height = 810;
3922 	blinder->autoWidth = AUTO_VIRTUALWIDTH;
3923 	blinder->autoHeight = AUTO_VIRTUALWIDTH;
3924 	blinder->scale = Vector(1.0125,1.0125);
3925 	blinder->followCamera = 1;
3926 
3927 	blinder->alpha = 0;
3928 	dsq->game->addRenderObject(blinder, LR_AFTER_EFFECTS);
3929 
3930 	tripper = new PauseQuad;
3931 	tripper->position = Vector(400,300);
3932 	tripper->setTexture("particles/tripper");
3933 	//tripper->setWidthHeight(810, 810);
3934 	tripper->autoWidth = AUTO_VIRTUALWIDTH;
3935 	tripper->autoHeight = AUTO_VIRTUALWIDTH;
3936 	tripper->scale = Vector(1.0125, 1.0125);
3937 	tripper->followCamera = 1;
3938 	tripper->alpha = 0;
3939 	dsq->game->addRenderObject(tripper, LR_AFTER_EFFECTS);
3940 
3941 	songIcons.resize(8);
3942 	int i = 0;
3943 	for (i = 0; i < songIcons.size(); i++)
3944 	{
3945 		songIcons[i] = new SongIcon(i);
3946 		songIcons[i]->alpha = 0;
3947 		songIcons[i]->followCamera = 1;
3948 		dsq->game->addRenderObject(songIcons[i], LR_HUD);
3949 	}
3950 
3951 	setSongIconPositions();
3952 
3953 	fader = new Quad;
3954 	fader->position = Vector(400,300);
3955 	fader->setTexture("fader");
3956 	fader->setWidthHeight(core->getVirtualWidth()+10);
3957 	fader->followCamera = 1;
3958 	fader->alpha = 0;
3959 	dsq->game->addRenderObject(fader, LR_AFTER_EFFECTS);
3960 
3961 	debugLog("Avatar 6");
3962 
3963 	targetQuads.resize(targets.size());
3964 
3965 	for (i = 0; i < targets.size(); i++)
3966 	{
3967 		targetQuads[i] = new ParticleEffect;
3968 		/*
3969 		targetQuads[i]->setTexture("missingImage");
3970 		targetQuads[i]->alpha = 0;
3971 		*/
3972 		targetQuads[i]->load("EnergyBlastTarget");
3973 
3974 		// HACK: should have its own layer?
3975 		dsq->game->addRenderObject(targetQuads[i], LR_PARTICLES);
3976 	}
3977 
3978 	lightFormGlow = new Quad("Naija/LightFormGlow", 0);
3979 	lightFormGlow->alpha = 0;
3980 
3981 	lightFormGlow->scale.interpolateTo(Vector(5.5, 5.5), 0.4, -1, 1);
3982 	//lightFormGlow->positionSnapTo = &position;
3983 	dsq->game->addRenderObject(lightFormGlow, LR_ELEMENTS13);
3984 
3985 	lightFormGlowCone = new Quad("Naija/LightFormGlowCone", 0);
3986 	lightFormGlowCone->alpha = 0;
3987 	lightFormGlowCone->scale = Vector(1, 6); // 4.5
3988 	dsq->game->addRenderObject(lightFormGlowCone, LR_ELEMENTS13);
3989 
3990 
3991 	debugLog("Avatar 7");
3992 
3993 	addChild(&regenEmitter, PM_STATIC);
3994 	regenEmitter.load("FoodEffectRegen");
3995 
3996 	addChild(&speedEmitter, PM_STATIC);
3997 	speedEmitter.load("FoodEffectSpeed");
3998 
3999 	addChild(&defenseEmitter, PM_STATIC);
4000 	defenseEmitter.load("FoodEffectDefense");
4001 
4002 	addChild(&invincibleEmitter, PM_STATIC);
4003 	invincibleEmitter.load("FoodEffectInvincible");
4004 
4005 	addChild(&auraEmitter, PM_STATIC);
4006 
4007 	addChild(&auraLowEmitter, PM_STATIC);
4008 
4009 	addChild(&auraHitEmitter, PM_STATIC);
4010 	auraHitEmitter.load("AuraShieldHit");
4011 
4012 	chargingEmitter = new ParticleEffect;
4013 	dsq->getTopStateData()->addRenderObject(chargingEmitter, LR_PARTICLES);
4014 
4015 	chargeEmitter = new ParticleEffect;
4016 	dsq->getTopStateData()->addRenderObject(chargeEmitter, LR_PARTICLES_TOP);
4017 
4018 	leftHandEmitter = new ParticleEffect;
4019 	dsq->getTopStateData()->addRenderObject(leftHandEmitter, LR_PARTICLES);
4020 
4021 	rightHandEmitter = new ParticleEffect;
4022 	dsq->getTopStateData()->addRenderObject(rightHandEmitter, LR_PARTICLES);
4023 
4024 	addChild(&biteLeftEmitter, PM_STATIC);
4025 	biteLeftEmitter.load("BiteLeft");
4026 
4027 	addChild(&biteRightEmitter, PM_STATIC);
4028 	biteRightEmitter.load("BiteRight");
4029 
4030 	addChild(&wakeEmitter, PM_STATIC);
4031 	wakeEmitter.load("Wake");
4032 
4033 	addChild(&swimEmitter, PM_STATIC);
4034 	swimEmitter.load("Swim");
4035 
4036 	addChild(&plungeEmitter, PM_STATIC);
4037 	plungeEmitter.load("Plunge");
4038 	plungeEmitter.position = Vector(0,-100);
4039 
4040 	addChild(&spiritBeaconEmitter, PM_STATIC);
4041 	spiritBeaconEmitter.load("SpiritBeacon");
4042 
4043 	addChild(&healEmitter, PM_STATIC);
4044 
4045 	addChild(&hitEmitter, PM_STATIC);
4046 
4047 	addChild(&rollLeftEmitter, PM_STATIC);
4048 
4049 	addChild(&rollRightEmitter, PM_STATIC);
4050 
4051 	rollRightEmitter.load("RollRight");
4052 	rollLeftEmitter.load("RollLeft");
4053 
4054 	debugLog("Avatar 8");
4055 
4056 	perform(STATE_IDLE);
4057 	skeletalSprite.animate(getIdleAnimName(),-1);
4058 
4059 	debugLog("Avatar 9");
4060 
4061 	setDamageTarget(DT_AVATAR_LANCE, false);
4062 
4063 	//changeForm(FORM_NORMAL, false);
4064 
4065 	refreshNormalForm();
4066 
4067 	if(dsq->continuity.form == FORM_FISH)
4068 		collideRadius = COLLIDE_RADIUS_FISH;
4069 	else
4070 		collideRadius = COLLIDE_RADIUS_NORMAL;
4071 
4072 	// defaults for normal form
4073 	_canActivateStuff = true;
4074 	_canBurst = true;
4075 	_canLockToWall = true;
4076 	_canSwimAgainstCurrents = false;
4077 	_canCollideWithShots = true;
4078 
4079 	_collisionAvoidMod = COLLIDE_MOD_NORMAL;
4080 	_collisionAvoidRange = COLLIDE_RANGE_NORMAL;
4081 
4082 	_seeMapMode = SEE_MAP_DEFAULT;
4083 
4084 	blockBackFlip = false;
4085 	elementEffectMult = 1;
4086 }
4087 
revert()4088 void Avatar::revert()
4089 {
4090 	if (canChangeForm)
4091 	{
4092 		if (dsq->continuity.form != FORM_NORMAL)
4093 			changeForm(FORM_NORMAL);
4094 	}
4095 }
4096 
onHeal(int type)4097 void Avatar::onHeal(int type)
4098 {
4099 	if (type == 1)
4100 	{
4101 		healEmitter.load("Heal");
4102 		healEmitter.start();
4103 	}
4104 }
4105 
refreshNormalForm()4106 void Avatar::refreshNormalForm()
4107 {
4108 	std::string c = dsq->continuity.costume;
4109 	if (c.empty())
4110 		c = "Naija";
4111 	refreshModel("Naija", c);
4112 	if(hair)
4113 	{
4114 		hair->alphaMod = 1.0;
4115 		if (!c.empty() && c!="Naija")
4116 		{
4117 			if(!hair->setTexture("naija/cape-"+c))
4118 				hair->alphaMod = 0;
4119 		}
4120 		else
4121 			hair->setTexture("naija/cape");
4122 	}
4123 }
4124 
refreshModel(std::string file,const std::string & skin,bool forceIdle)4125 void Avatar::refreshModel(std::string file, const std::string &skin, bool forceIdle)
4126 {
4127 	stringToLower(file);
4128 
4129 	bool loadedSkeletal = false;
4130 
4131 	if (!skeletalSprite.isLoaded() || nocasecmp(skeletalSprite.filenameLoaded, file)!=0)
4132 	{
4133 		skeletalSprite.loadSkeletal(file);
4134 		loadedSkeletal = true;
4135 	}
4136 	if (!skin.empty())
4137 		skeletalSprite.loadSkin(skin);
4138 
4139 	if (file == "beast")
4140 	{
4141 		skeletalSprite.scale = Vector(1.25,1.25);
4142 	}
4143 	else
4144 		skeletalSprite.scale = Vector(1,1);
4145 
4146 	Animation *anim = skeletalSprite.getCurrentAnimation(0);
4147 	if (forceIdle || (!anim || loadedSkeletal))
4148 	{
4149 		idle();
4150 	}
4151 
4152 	if (file == "naija")
4153 	{
4154 		bone_head = skeletalSprite.getBoneByIdx(1);
4155 		boneRightFoot = skeletalSprite.getBoneByName("RightFoot");
4156 		boneLeftFoot = skeletalSprite.getBoneByName("LeftFoot");
4157 		boneRightArm = skeletalSprite.getBoneByName("RightArm");
4158 		boneLeftArm = skeletalSprite.getBoneByName("LeftArm");
4159 		boneFish2 = skeletalSprite.getBoneByName("Fish2");
4160 		boneFish2->alpha = 0;
4161 		bone_dualFormGlow = skeletalSprite.getBoneByName("DualFormGlow");
4162 		bone_dualFormGlow->scale = 0;
4163 		bone_dualFormGlow->setBlendType(BLEND_ADD);
4164 
4165 		boneLeftHand = skeletalSprite.getBoneByName("LeftArm");
4166 		boneRightHand = skeletalSprite.getBoneByName("RightArm");
4167 	}
4168 	else
4169 	{
4170 		bone_dualFormGlow = 0;
4171 		bone_head = 0;
4172 		boneRightFoot = boneLeftFoot = boneRightArm = boneLeftArm = boneFish2 = skeletalSprite.getBoneByIdx(0);
4173 		boneLeftHand = boneRightHand = 0;
4174 	}
4175 
4176 	core->resetTimer();
4177 
4178 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
4179 }
4180 
~Avatar()4181 Avatar::~Avatar()
4182 {
4183 	songIcons.clear();
4184 }
4185 
destroy()4186 void Avatar::destroy()
4187 {
4188 	Entity::destroy();
4189 
4190 	if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
4191 	{
4192 		core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
4193 		dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
4194 	}
4195 	if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
4196 	{
4197 		core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
4198 		dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
4199 	}
4200 
4201 	avatar = 0;
4202 }
4203 
startBackFlip()4204 void Avatar::startBackFlip()
4205 {
4206 	if (boneLock.on) return;
4207 	if (riding) return;
4208 	if (blockBackFlip) return;
4209 
4210 	skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip", 0.2, 0);
4211 	vel.x = -vel.x*0.25f;
4212 	state.backFlip = true;
4213 }
4214 
stopBackFlip()4215 void Avatar::stopBackFlip()
4216 {
4217 	if (state.backFlip)
4218 	{
4219 		//skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
4220 		skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip2", 0.2, 0);
4221 		state.backFlip = false;
4222 	}
4223 }
4224 
startBurstCommon()4225 void Avatar::startBurstCommon()
4226 {
4227 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
4228 
4229 	bittenEntities.clear();
4230 	flourish = false;
4231 	if (dsq->continuity.form == FORM_BEAST)
4232 	{
4233 		setHeadTexture("Bite");
4234 	}
4235 	burstTimer = 0;
4236 
4237 	setBoneLock(BoneLock());
4238 
4239 	biteTimer = 0;
4240 
4241 	if (dsq->continuity.form == FORM_BEAST)
4242 	{
4243 		if (isfh())
4244 			biteRightEmitter.start();
4245 		else
4246 			biteLeftEmitter.start();
4247 	}
4248 }
4249 
startBurst()4250 void Avatar::startBurst()
4251 {
4252 	if (!riding && canBurst() && (joystickMove || getVectorToCursor().getSquaredLength2D() > sqr(BURST_DISTANCE))
4253 		&& getState() != STATE_PUSH && (!skeletalSprite.getCurrentAnimation() || (skeletalSprite.getCurrentAnimation()->name != "spin"))
4254 		&& _isUnderWater && !isActing(ACTION_ROLL))
4255 	{
4256 		if (!bursting && burst == 1)
4257 		{
4258 			dsq->rumble(0.2, 0.2, 0.2);
4259 			if (dsq->continuity.form != FORM_BEAST)
4260 				wakeEmitter.start();
4261 			dsq->game->playBurstSound(pushingOffWallEffect>0);
4262 			skeletalSprite.animate(getBurstAnimName(), 0);
4263 			bursting = true;
4264 			burst = 1.0;
4265 			ripples = true;
4266 			startBurstCommon();
4267 
4268 			lastBurstType = BURST_NORMAL;
4269 		}
4270 		else if (bursting && burstTimer > 0.3f)
4271 		{
4272 			if (!flourish && !state.nearWall)
4273 				//&& dsq->continuity.form == FORM_NORMAL)
4274 			{
4275 				//if (rand()%100 < 50)
4276 				if (true)
4277 				{
4278 					startFlourish();
4279 				}
4280 				/*
4281 				else
4282 				{
4283 					skeletalSprite.transitionAnimate("flourish2", 0.1, 0, 3);
4284 					flourish = true;
4285 					if (this->isfh())
4286 						rotationOffset = Vector(0,0,-360);
4287 					else
4288 						rotationOffset = Vector(0,0,360);
4289 					rotationOffset.interpolateTo(Vector(0,0,0), 0.8, 0, 0, 1);
4290 				}
4291 				*/
4292 				//burst += 0.1;
4293 				if (!vel.isZero())
4294 				{
4295 					Vector add = vel;
4296 					add.setLength2D(50);
4297 					vel2 += add;
4298 				}
4299 			}
4300 		}
4301 	}
4302 }
4303 
startWallBurst(bool useCursor)4304 void Avatar::startWallBurst(bool useCursor)
4305 {
4306 	//if (!bursting && burst == 1 )
4307 	{
4308 		Vector goDir;
4309 
4310 		//goDir = getVectorToCursorFromScreenCentre();
4311 		goDir = getVectorToCursor();
4312 
4313 		if (goDir.isLength2DIn(BURST_DISTANCE))
4314 		{
4315 			if (!goDir.isLength2DIn(minMouse))
4316 				fallOffWall();
4317 			return;
4318 		}
4319 		goDir.normalize2D();
4320 
4321 		if (_isUnderWater && dsq->continuity.form != FORM_BEAST)
4322 			wakeEmitter.start();
4323 
4324 		offset.interpolateTo(Vector(0,0), 0.05);
4325 
4326 		dsq->spawnParticleEffect("WallBoost", position+offset, rotation.z);
4327 		if (goDir.x != 0 || goDir.y != 0)
4328 		{
4329 			lastBurstType = BURST_WALL;
4330 
4331 			dsq->rumble(0.22, 0.22, 0.2);
4332 			bittenEntities.clear();
4333 			if (useCursor)
4334 			{
4335 				wallPushVec = (goDir*0.75f + wallNormal*0.25f);
4336 				//wallPushVec = goDir;
4337 			}
4338 			else
4339 			{
4340 				float v = goDir.dot2D(wallNormal);
4341 				if (v <= -0.8f)
4342 					wallPushVec = wallNormal;
4343 				else
4344 				{
4345 					wallPushVec = (goDir*0.5f + wallNormal*0.5f);
4346 				}
4347 			}
4348 				//wallPushVec = (goDir*0.9f + wallNormal*0.1f);
4349 			wallPushVec.setLength2D(vars->maxWallJumpBurstSpeed);
4350 
4351 			position.stop();
4352 			pushingOffWallEffect = 0.5;
4353 			vel = wallPushVec;
4354 			this->state.lockedToWall = false;
4355 			skeletalSprite.stopAllAnimations();
4356 			dsq->game->playBurstSound(pushingOffWallEffect>0);
4357 			skeletalSprite.animate(getBurstAnimName(), 0);
4358 			bursting = true;
4359 			burst = 1.5;
4360 			ripples = true;
4361 
4362 			startBurstCommon();
4363 		}
4364 	}
4365 }
4366 
getKeyDir()4367 Vector Avatar::getKeyDir()
4368 {
4369 	Vector dir;
4370 	if (isActing(ACTION_SWIMLEFT))
4371 		dir += Vector(-1,0);
4372 	if (isActing(ACTION_SWIMRIGHT))
4373 		dir += Vector(1,0);
4374 	if (isActing(ACTION_SWIMUP))
4375 		dir += Vector(0,-1);
4376 	if (isActing(ACTION_SWIMDOWN))
4377 		dir += Vector(0,1);
4378 
4379 	if (dir.x != 0 && dir.y != 0)
4380 		dir/=2;
4381 
4382 	return dir;
4383 }
4384 
getFakeCursorPosition()4385 Vector Avatar::getFakeCursorPosition()
4386 {
4387 	if (dsq->inputMode == INPUT_KEYBOARD)
4388 	{
4389 		return getKeyDir() * 350;
4390 	}
4391 	if (dsq->inputMode == INPUT_JOYSTICK)
4392 	{
4393 		const float axisInput = core->joystick.position.getLength2D();
4394 		if (axisInput < JOYSTICK_LOW_THRESHOLD)
4395 		{
4396 			return Vector(0,0,0);
4397 		}
4398 		else
4399 		{
4400 			const float axisMult = (maxMouse - minMouse) / (JOYSTICK_HIGH_THRESHOLD - JOYSTICK_LOW_THRESHOLD);
4401 			const float distance = minMouse + ((axisInput - JOYSTICK_LOW_THRESHOLD) * axisMult);
4402 			return (core->joystick.position * (distance / axisInput));
4403 		}
4404 	}
4405 	return Vector(0,0,0);
4406 }
4407 
getVectorToCursorFromScreenCentre()4408 Vector Avatar::getVectorToCursorFromScreenCentre()
4409 {
4410 	if (game->cameraOffBounds)
4411 		return getVectorToCursor();
4412 	else
4413 	{
4414 		if (dsq->inputMode != INPUT_MOUSE)
4415 			return getFakeCursorPosition();
4416 		return (core->mouse.position+offset) - Vector(400,300);
4417 	}
4418 }
4419 
getVectorToCursor(bool trueMouse)4420 Vector Avatar::getVectorToCursor(bool trueMouse)
4421 {
4422 	//return getVectorToCursorFromScreenCentre();
4423 	Vector pos = dsq->getGameCursorPosition();
4424 
4425 
4426 	if (!trueMouse && dsq->inputMode != INPUT_MOUSE)
4427 		return getFakeCursorPosition();
4428 
4429 	return pos - (position+offset);
4430 	//return core->mouse.position - Vector(400,300);
4431 }
4432 
action(int id,int state)4433 void Avatar::action(int id, int state)
4434 {
4435 	if(dsq->game->isIgnoreAction((AquariaActions)id))
4436 		return;
4437 
4438 	if (id == ACTION_PRIMARY)	{ if (state) lmbd(); else lmbu(); }
4439 	if (id == ACTION_SECONDARY) { if (state) rmbd(); else rmbu(); }
4440 
4441 	if (id == ACTION_REVERT && !state)
4442 		revert();
4443 
4444 	if (id == ACTION_PRIMARY && state)// !state
4445 	{
4446 		if (dsq->isMiniMapCursorOkay())
4447 		{
4448 			if (this->state.lockedToWall)
4449 			{
4450 				Vector test = getVectorToCursor();
4451 				if (test.isLength2DIn(minMouse))
4452 				{
4453 					fallOffWall();
4454 				}
4455 				else
4456 				{
4457 					if (boneLock.entity)
4458 						wallNormal = boneLock.wallNormal;
4459 
4460 					if (isUnderWater())
4461 					{
4462 						test.normalize2D();
4463 						float dott = wallNormal.dot2D(test);
4464 						burst = 1;
4465 						bursting = false;
4466 						// normal is 90 degrees within/on the right side
4467 						if (dott > 0)
4468 						{
4469 							startWallBurst(true);
4470 						}
4471 						else
4472 						{
4473 							startWallBurst(false);
4474 						}
4475 					}
4476 					else
4477 					{
4478 						test.normalize2D();
4479 						float dott = wallNormal.dot2D(test);
4480 
4481 						if (dott > -0.3f)
4482 						{
4483 							burst = 1;
4484 							bursting = false;
4485 							startWallBurst(true);
4486 						}
4487 						else
4488 						{
4489 							// nothing
4490 						}
4491 					}
4492 				}
4493 
4494 			}
4495 			else
4496 			{
4497 				startBurst();
4498 				// FIXME: This is a quick hack to make sure the burst
4499 				// transition animation is played when using joystick or
4500 				// keyboard control.  The same thing should probably be
4501 				// done for wall bursts, but the movement there is fast
4502 				// enough that people probably won't notice, so I skipped
4503 				// that.  Sorry about the ugliness.  --achurch
4504 				if (dsq->inputMode != INPUT_MOUSE)
4505 					skeletalSprite.transitionAnimate("swim", ANIM_TRANSITION, -1);
4506 			}
4507 		}
4508 	}
4509 
4510 	else if (id >= ACTION_SONGSLOT1 && id < ACTION_SONGSLOTEND)
4511 	{
4512 		if (canQuickSong())
4513 		{
4514 			int count = (id - ACTION_SONGSLOT1)+1;
4515 
4516 			bool cast = false;
4517 
4518 			if (dsq->continuity.form == FORM_SPIRIT)
4519 			{
4520 				revert();
4521 				cast = true;
4522 			}
4523 			else
4524 			{
4525 				switch(count)
4526 				{
4527 				case 1:
4528 				{
4529 					if (dsq->continuity.form != FORM_NORMAL)
4530 					{
4531 						revert();
4532 						cast = true;
4533 					}
4534 				}
4535 				break;
4536 				case 2:
4537 					if (dsq->continuity.form != FORM_ENERGY)
4538 					{
4539 						dsq->continuity.castSong(SONG_ENERGYFORM);
4540 						cast = true;
4541 					}
4542 				break;
4543 				case 3:
4544 					if (dsq->continuity.form != FORM_BEAST)
4545 					{
4546 						dsq->continuity.castSong(SONG_BEASTFORM);
4547 						cast = true;
4548 					}
4549 				break;
4550 				case 4:
4551 					if (dsq->continuity.form != FORM_NATURE)
4552 					{
4553 						dsq->continuity.castSong(SONG_NATUREFORM);
4554 						cast = true;
4555 					}
4556 				break;
4557 				case 5:
4558 					if (dsq->continuity.form != FORM_SUN)
4559 					{
4560 						dsq->continuity.castSong(SONG_SUNFORM);
4561 						cast = true;
4562 					}
4563 				break;
4564 				case 6:
4565 					if (dsq->continuity.form != FORM_FISH)
4566 					{
4567 						dsq->continuity.castSong(SONG_FISHFORM);
4568 						cast = true;
4569 					}
4570 				break;
4571 				case 7:
4572 					if (dsq->continuity.form != FORM_SPIRIT)
4573 					{
4574 						dsq->continuity.castSong(SONG_SPIRITFORM);
4575 						cast = true;
4576 					}
4577 				break;
4578 				case 8:
4579 					if (dsq->continuity.form != FORM_DUAL)
4580 					{
4581 						dsq->continuity.castSong(SONG_DUALFORM);
4582 						cast = true;
4583 					}
4584 				break;
4585 				default:
4586 					if (dsq->continuity.form == FORM_NORMAL)
4587 					{
4588 						switch(count)
4589 						{
4590 						case 9:
4591 						{
4592 							dsq->continuity.castSong(SONG_SHIELDAURA);
4593 							cast = true;
4594 						}
4595 						break;
4596 						case 10:
4597 						{
4598 							dsq->continuity.castSong(SONG_BIND);
4599 							cast = true;
4600 						}
4601 						break;
4602 						}
4603 					}
4604 				break;
4605 				}
4606 			}
4607 
4608 
4609 			if (cast)
4610 			{
4611 				quickSongCastDelay = QUICK_SONG_CAST_DELAY;
4612 			}
4613 		}
4614 	}
4615 }
4616 
doBindSong()4617 void Avatar::doBindSong()
4618 {
4619 	if (pullTarget)
4620 	{
4621 		pullTarget->stopPull();
4622 		pullTarget = 0;
4623 		core->sound->playSfx("Denied");
4624 	}
4625 	else
4626 	{
4627 		dsq->game->bindIngredients();
4628 		setNearestPullTarget();
4629 		if (!pullTarget)
4630 		{
4631 			core->sound->playSfx("Denied");
4632 		}
4633 		else
4634 		{
4635 			core->sound->playSfx("Bind");
4636 		}
4637 	}
4638 }
4639 
doShieldSong()4640 void Avatar::doShieldSong()
4641 {
4642 	core->sound->playSfx("Shield-On");
4643 	activateAura(AURA_SHIELD);
4644 }
4645 
render()4646 void Avatar::render()
4647 {
4648 
4649 	if (dsq->continuity.form == FORM_SPIRIT && !skeletalSprite.getParent())
4650 	{
4651 		skeletalSprite.position = bodyPosition+bodyOffset;
4652 		skeletalSprite.color = Vector(0.2, 0.3, 0.6);
4653 		skeletalSprite.render();
4654 		skeletalSprite.color = Vector(1,1,1);
4655 	}
4656 
4657 	Entity::render();
4658 
4659 }
4660 
onRender()4661 void Avatar::onRender()
4662 {
4663 	Entity::onRender();
4664 }
4665 
getBeamWidth()4666 int Avatar::getBeamWidth()
4667 {
4668 	const int MAX_BEAM_LEN = 50;
4669 	Vector mov = dsq->getGameCursorPosition() - this->position;
4670 	mov.setLength2D(1);
4671 	TileVector t(position);
4672 	Vector tile(t.x, t.y);
4673 	int c = 0;
4674 	while (c < MAX_BEAM_LEN)
4675 	{
4676 		bool hit = false;
4677 		tile += mov;
4678 		TileVector t;
4679 		t.x = int(tile.x);
4680 		t.y = int(tile.y);
4681 		if (dsq->game->isObstructed(t))
4682 		{
4683 			hit = true;
4684 		}
4685 
4686 		FOR_ENTITIES(i)
4687 		{
4688 			Entity *e = *i;
4689 			if (e != this)
4690 			{
4691 				TileVector et(e->position);
4692 				Vector t1(et.x, et.y);
4693 				Vector t2(tile.x, tile.y);
4694 				Vector diff = t1-t2;
4695 				if (diff.getSquaredLength2D() <= 1)
4696 				{
4697 					// HACK: replace damage function
4698 					//e->damage(1, 0, this);
4699 					hit = true;
4700 				}
4701 			}
4702 		}
4703 		if (hit)
4704 			break;
4705 		c++;
4706 	}
4707 	return c * TILE_SIZE;
4708 }
4709 
onEnterState(int action)4710 void Avatar::onEnterState(int action)
4711 {
4712 	Entity::onEnterState(action);
4713 	if (action == STATE_PUSH)
4714 	{
4715 		state.lockedToWall = false;
4716 		Animation *a = skeletalSprite.getCurrentAnimation();
4717 		if (!a || (a && a->name != "pushed"))
4718 			skeletalSprite.animate("pushed", 0);
4719 	}
4720 }
4721 
onExitState(int action)4722 void Avatar::onExitState(int action)
4723 {
4724 	Entity::onExitState(action);
4725 	if (action == STATE_TRANSFORM)
4726 	{
4727 		setState(STATE_IDLE);
4728 	}
4729 	else if (action == STATE_PUSH)
4730 	{
4731 		skeletalSprite.transitionAnimate("spin", 0.1);
4732 	}
4733 }
4734 
splash(bool down)4735 void Avatar::splash(bool down)
4736 {
4737 	if (splashDelay > 0)
4738 	{
4739 		lastJumpOutFromWaterBubble = false;
4740 		return;
4741 	}
4742 	splashDelay = SPLASH_INTERVAL;
4743 
4744 	if (down)
4745 	{
4746 		sound("splash-into", rolling ? 0.9f : 1.0f);
4747 		//dsq->postProcessingFx.disable(FXT_RADIALBLUR);
4748 		if (_isUnderWater && core->afterEffectManager)
4749 			core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
4750 		dsq->rumble(0.7, 0.7, 0.2);
4751 		plungeEmitter.start();
4752 
4753 		core->sound->playSfx("GoUnder");
4754 	}
4755 	else
4756 	{
4757 		sound("splash-outof");
4758 		/*
4759 		dsq->postProcessingFx.enable(FXT_RADIALBLUR);
4760 		dsq->postProcessingFx.radialBlurColor = Vector(1,1,1);
4761 		dsq->postProcessingFx.intensity = 0.1;
4762 		*/
4763 
4764 		core->sound->playSfx("Emerge");
4765 	}
4766 	// make a splash effect @ current position
4767 
4768 	// FIXME: This is broken for waterfalls/bubbles (e.g. the waterfalls
4769 	// in the Turtle Cave).  Not sure how best to fix it in the current
4770 	// code.  --achurch
4771 	Vector hsplash = avatar->getHeadPosition();
4772 	hsplash.y = dsq->game->waterLevel.x;
4773 	core->createParticleEffect("HeadSplash", hsplash, LR_PARTICLES);
4774 
4775 	float a = 0;
4776 
4777 	if (waterBubble || lastJumpOutFromWaterBubble)
4778 	{
4779 		lastJumpOutFromWaterBubble = false;
4780 
4781 		if (waterBubble)
4782 		{
4783 			Vector diff = position - waterBubble->nodes[0].position;
4784 			a = MathFunctions::getAngleToVector(diff, 180);
4785 		}
4786 		else if (lastWaterBubble)
4787 		{
4788 			Vector diff = position - lastWaterBubble->nodes[0].position;
4789 			a = MathFunctions::getAngleToVector(diff, 0);
4790 		}
4791 		else
4792 			a = MathFunctions::getAngleToVector(vel+vel2, 0);
4793 	}
4794 
4795 	dsq->spawnParticleEffect("Splash", position, a);
4796 }
4797 
clampVelocity()4798 void Avatar::clampVelocity()
4799 {
4800 	/*
4801 	std::ostringstream os;
4802 	os << "currentMaxSpeed: " << currentMaxSpeed;
4803 	debugLog(os.str());
4804 	*/
4805 	float useSpeedMult = dsq->continuity.speedMult;
4806 	bool inCurrent = isInCurrent();
4807 	bool withCurrent = false;
4808 
4809 	if (inCurrent)
4810 	{
4811 		// if vel2 and vel are pointing the same way
4812 		if (vel2.dot2D(vel) > 0)
4813 		{
4814 			withCurrent = true;
4815 		}
4816 	}
4817 
4818 	if (!inCurrent || (inCurrent && withCurrent))
4819 	{
4820 		if (dsq->continuity.form == FORM_FISH)
4821 		{
4822 			useSpeedMult *= MULT_MAXSPEED_FISHFORM;
4823 		}
4824 	}
4825 	else
4826 	{
4827 		useSpeedMult = 1;
4828 	}
4829 
4830 	if (dsq->continuity.form == FORM_BEAST)
4831 	{
4832 		useSpeedMult *= MULT_MAXSPEED_BEASTFORM;
4833 	}
4834 
4835 	if (currentState == STATE_PUSH)
4836 	{
4837 		currentMaxSpeed = pushMaxSpeed;
4838 	}
4839 
4840 
4841 	setMaxSpeed(currentMaxSpeed * useSpeedMult * dsq->continuity.speedMult2);
4842 
4843 	vel.capLength2D(getMaxSpeed() /* * maxSpeedLerp.x*/);
4844 }
4845 
activateAura(AuraType aura)4846 void Avatar::activateAura(AuraType aura)
4847 {
4848 	activeAura = aura;
4849 	auraTimer = 30;
4850 	if (aura == AURA_SHIELD)
4851 	{
4852 		shieldPoints = maxShieldPoints;
4853 		if (auraLowEmitter.isRunning())
4854 			auraLowEmitter.stop();
4855 		auraEmitter.load("AuraShield");
4856 		auraEmitter.start();
4857 		if (dsq->loops.shield == BBGE_AUDIO_NOCHANNEL)
4858 		{
4859 			PlaySfx play;
4860 			play.name = "Shield-Loop";
4861 			play.fade = SFT_IN;
4862 			play.time = 1;
4863 			play.loops = -1;
4864 			dsq->loops.shield = core->sound->playSfx(play);
4865 		}
4866 	}
4867 }
4868 
updateAura(float dt)4869 void Avatar::updateAura(float dt)
4870 {
4871 	if (auraTimer > 0 && dsq->continuity.form != FORM_SPIRIT)
4872 	{
4873 		switch(activeAura)
4874 		{
4875 		case AURA_SHIELD:
4876 		{
4877 			//shieldPosition = position + Vector(cosf(auraTimer*4)*100, sinf(auraTimer*4)*100);
4878 			shieldPosition = position;
4879 			/*
4880 			float a = ((rotation.z)*PI)/180.0f + PI*0.5f;
4881 			shieldPosition = position + Vector(cosf(a)*100, sinf(a)*100);
4882 			*/
4883 			for (Shot::Shots::iterator i = Shot::shots.begin(); i != Shot::shots.end(); ++i)
4884 			{
4885 				Shot *s = *i;
4886 				if (s->isActive() && dsq->game->isDamageTypeEnemy(s->getDamageType()) && s->firer != this
4887 					&& (!s->shotData || !s->shotData->ignoreShield))
4888 				{
4889 
4890 					Vector diff = s->position - shieldPosition;
4891 					if (diff.getSquaredLength2D() < sqr(AURA_SHIELD_RADIUS))
4892 					{
4893 						shieldPoints -= s->getDamage();
4894 						auraHitEmitter.start();
4895 						dsq->spawnParticleEffect("ReflectShot", s->position);
4896 						core->sound->playSfx("Shield-Hit");
4897 						s->position += diff;
4898 						//s->target = 0;
4899 						diff.setLength2D(s->maxSpeed);
4900 						s->velocity = diff;
4901 						s->reflectFromEntity(this);
4902 					}
4903 				}
4904 			}
4905 		}
4906 		break;
4907 		}
4908 
4909 		auraTimer -= dt;
4910 		if (auraTimer < 5 || shieldPoints < 2)
4911 		{
4912 			if (auraEmitter.isRunning())
4913 			{
4914 				auraEmitter.stop();
4915 				auraLowEmitter.load("AuraShieldLow");
4916 				auraLowEmitter.start();
4917 			}
4918 		}
4919 
4920 		if (auraTimer < 0 || shieldPoints < 0)
4921 		{
4922 			stopAura();
4923 		}
4924 	}
4925 }
4926 
stopAura()4927 void Avatar::stopAura()
4928 {
4929 	auraTimer = 0;
4930 	activeAura = AURA_NONE;
4931 	auraEmitter.stop();
4932 	auraLowEmitter.stop();
4933 	if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
4934 	{
4935 		core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
4936 		dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
4937 	}
4938 }
4939 
setHeadTexture(const std::string & name,float time)4940 void Avatar::setHeadTexture(const std::string &name, float time)
4941 {
4942 	if (!bone_head) return;
4943 	if (dsq->continuity.form == FORM_NORMAL /*&& dsq->continuity.costume.empty()*/)
4944 	{
4945 		if (!name.empty() && (nocasecmp(lastHeadTexture, "singing")==0)) return;
4946 
4947 		lastHeadTexture = name;
4948 		stringToUpper(lastHeadTexture);
4949 		std::string t = "Naija/";
4950 
4951 		if (!dsq->continuity.costume.empty())
4952 		{
4953 			if (nocasecmp(dsq->continuity.costume, "end")==0)
4954 				t += "Naija2";
4955 			else
4956 				t += dsq->continuity.costume;
4957 		}
4958 		else if (dsq->continuity.form == FORM_BEAST)
4959 		{
4960 			t += "Beast";
4961 		}
4962 		else
4963 		{
4964 			t += "Naija2";
4965 		}
4966 		t += "-Head";
4967 		if (!name.empty())
4968 		{
4969 			t += "-" + name;
4970 		}
4971 		bone_head->setTexture(t);
4972 
4973 		headTextureTimer = time;
4974 	}
4975 }
4976 
chargeVisualEffect(const std::string & tex)4977 void Avatar::chargeVisualEffect(const std::string &tex)
4978 {
4979 	float time = 0.4;
4980 	Quad *chargeEffect = new Quad;
4981 	chargeEffect->setBlendType(BLEND_ADD);
4982 	chargeEffect->alpha.ensureData();
4983 	chargeEffect->alpha.data->path.addPathNode(0, 0);
4984 	chargeEffect->alpha.data->path.addPathNode(0.6, 0.1);
4985 	chargeEffect->alpha.data->path.addPathNode(0.6, 0.9);
4986 	chargeEffect->alpha.data->path.addPathNode(0, 1.0);
4987 	chargeEffect->alpha.startPath(time);
4988 	chargeEffect->setTexture(tex);
4989 	//chargeEffect->positionSnapTo = &this->position;
4990 	chargeEffect->position = this->position;
4991 	chargeEffect->setPositionSnapTo(&position);
4992 	chargeEffect->setLife(1);
4993 	chargeEffect->setDecayRate(1.0f/time);
4994 	chargeEffect->scale = Vector(0.1, 0.1);
4995 	chargeEffect->scale.interpolateTo(Vector(1,1),time);
4996 	//chargeEffect->rotation.interpolateTo(Vector(0,0,360), time);
4997 	dsq->game->addRenderObject(chargeEffect, LR_PARTICLES);
4998 }
4999 
updateFormVisualEffects(float dt)5000 void Avatar::updateFormVisualEffects(float dt)
5001 {
5002 	switch (dsq->continuity.form)
5003 	{
5004 	case FORM_ENERGY:
5005 	{
5006 		Vector hairDir(96, -96);
5007 		if (this->isfh())
5008 		{
5009 			hairDir.x = -hairDir.x;
5010 		}
5011 	}
5012 	break;
5013 	case FORM_SUN:
5014 	{
5015 		lightFormGlow->position = this->position;
5016 		lightFormGlowCone->position = this->position;
5017 
5018 		float angle=0;
5019 		MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), getVectorToCursorFromScreenCentre(), angle);
5020 		angle = 180-(360-angle);
5021 
5022 		//lightFormGlowCone->rotation.interpolateTo(Vector(0,0,angle), 0.1);
5023 		lightFormGlowCone->rotation = Vector(0,0,angle);
5024 
5025 		static float lfgTimer = 0;
5026 		lfgTimer += dt;
5027 		if (lfgTimer > 0.5f)
5028 		{
5029 			//debugLog("lightFormGlow to front");
5030 			lightFormGlow->moveToFront();
5031 			lightFormGlowCone->moveToFront();
5032 			lfgTimer = 0;
5033 		}
5034 
5035 		if (this->isInDarkness())
5036 		{
5037 			lightFormGlowCone->alphaMod = 1;
5038 			lightFormGlow->alphaMod = 1;
5039 		}
5040 		else
5041 		{
5042 			lightFormGlow->alphaMod = 0;
5043 			lightFormGlowCone->alphaMod = 0;
5044 		}
5045 	}
5046 	break;
5047 	case FORM_SPIRIT:
5048 		skeletalSprite.update(dt);
5049 		skeletalSprite.position = bodyPosition;
5050 	break;
5051 	}
5052 }
5053 
stopBurst()5054 void Avatar::stopBurst()
5055 {
5056 	burst = 0;
5057 	//burstDelay = BURST_DELAY;
5058 	burstDelay = 0;
5059 	bursting = false;
5060 	wakeEmitter.stop();
5061 	ripples = false;
5062 
5063 	biteLeftEmitter.stop();
5064 	biteRightEmitter.stop();
5065 }
5066 
getCursorQuadrant()5067 int Avatar::getCursorQuadrant()
5068 {
5069 	//Vector diff = getVectorToCursorFromScreenCentre();
5070 	Vector diff = getVectorToCursor();
5071 	if (diff.isLength2DIn(40))
5072 	{
5073 		stopRoll();
5074 		return -999;
5075 	}
5076 
5077 	if (diff.y < 0)
5078 		return diff.x < 0 ? 4 : 1;
5079 	else
5080 		return diff.x < 0 ? 3 : 2;
5081 }
5082 
getQuadrantDirection(int lastQuad,int quad)5083 int Avatar::getQuadrantDirection(int lastQuad, int quad)
5084 {
5085 	int diff = quad - lastQuad;
5086 	if ((lastQuad==4 && quad == 1))
5087 	{
5088 		diff = 1;
5089 	}
5090 	if (lastQuad==1 && quad==4)
5091 	{
5092 		diff = -1;
5093 	}
5094 	if (abs(diff) != 1)
5095 		diff = 0;
5096 	return diff;
5097 }
5098 
startRoll(int dir)5099 void Avatar::startRoll(int dir)
5100 {
5101 	if (!rolling && !state.backFlip)
5102 	{
5103 		if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
5104 		{
5105 			rollRightEmitter.load("EnergyRollRight");
5106 			rollLeftEmitter.load("EnergyRollLeft");
5107 		}
5108 		else
5109 		{
5110 			rollRightEmitter.load("RollRight");
5111 			rollLeftEmitter.load("RollLeft");
5112 		}
5113 	}
5114 
5115 	Animation *a = skeletalSprite.getCurrentAnimation();
5116 	if (!a || a->name != getRollAnimName())
5117 	{
5118 		skeletalSprite.transitionAnimate(getRollAnimName(), 0.2, -1);
5119 	}
5120 
5121 	rollRightEmitter.stop();
5122 	rollLeftEmitter.stop();
5123 
5124 	if (_isUnderWater)
5125 	{
5126 		if (dir >= 1)
5127 			rollRightEmitter.start();
5128 
5129 		if (dir <= -1)
5130 			rollLeftEmitter.start();
5131 	}
5132 
5133 	//dsq->playVisualEffect(VFX_RIPPLE, Vector());
5134 	rolling = true;
5135 
5136 	if (dsq->loops.roll == BBGE_AUDIO_NOCHANNEL && _isUnderWater)
5137 	{
5138 		PlaySfx play;
5139 		play.name = "RollLoop";
5140 		play.fade = SFT_IN;
5141 		play.time = 1;
5142 		play.vol = 1;
5143 		play.loops = -1;
5144 		dsq->loops.roll = core->sound->playSfx(play);
5145 	}
5146 	else if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL && !_isUnderWater)
5147 	{
5148 		core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 0.5);
5149 	}
5150 
5151 	rollDir = dir;
5152 
5153 	if (_isUnderWater && core->afterEffectManager)
5154 		core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
5155 
5156 
5157 	//rollDelay = 0.3;
5158 }
5159 
stopRoll()5160 void Avatar::stopRoll()
5161 {
5162 	rolling = false;
5163 	lastQuadDir = lastQuad = 0;
5164 	rollDelay = 0;
5165 	rollDidOne = 0;
5166 	rollLeftEmitter.stop();
5167 	rollRightEmitter.stop();
5168 	state.rollTimer = 0;
5169 
5170 	if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL)
5171 	{
5172 		core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 1);
5173 		dsq->loops.roll = BBGE_AUDIO_NOCHANNEL;
5174 	}
5175 }
5176 
5177 
stopWallJump()5178 void Avatar::stopWallJump()
5179 {
5180 	wallBurstTimer = 0;
5181 }
5182 
updateWallJump(float dt)5183 void Avatar::updateWallJump(float dt)
5184 {
5185 	if (wallBurstTimer > 0)
5186 	{
5187 		wallBurstTimer -= dt;
5188 		if (wallBurstTimer < 0)
5189 		{
5190 			// wall jump failed!
5191 			stopWallJump();
5192 		}
5193 	}
5194 }
5195 
updateRoll(float dt)5196 void Avatar::updateRoll(float dt)
5197 {
5198 	if (!inputEnabled || dsq->game->isWorldPaused() || riding)
5199 	{
5200 		if (rolling)
5201 			stopRoll();
5202 		return;
5203 	}
5204 	if (state.lockedToWall || isSinging()) return;
5205 
5206 	if (rollDelay > 0)
5207 	{
5208 		rollDelay -= dt;
5209 		if (rollDelay <= 0)
5210 		{
5211 			// stop the animation
5212 			stopRoll();
5213 		}
5214 	}
5215 
5216 	if (!_isUnderWater && isActing(ACTION_ROLL))
5217 	{
5218 		stopRoll();
5219 	}
5220 
5221 	if (!core->mouse.buttons.left && dsq->inputMode == INPUT_MOUSE && !isActing(ACTION_ROLL))
5222 	{
5223 		if (rolling)
5224 			stopRoll();
5225 		return;
5226 	}
5227 
5228 	if (rolling)
5229 	{
5230 		if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
5231 		{
5232 			FOR_ENTITIES(i)
5233 			{
5234 				Entity *e = *i;
5235 				if (e->getEntityType() == ET_ENEMY && (e->position - this->position).isLength2DIn(256) && e->isDamageTarget(DT_AVATAR_ENERGYROLL))
5236 				{
5237 					DamageData d;
5238 					d.damage = dt*15;
5239 					d.damageType = DT_AVATAR_ENERGYROLL;
5240 					d.attacker = this;
5241 					e->damage(d);
5242 				}
5243 			}
5244 		}
5245 
5246 		state.rollTimer += dt;
5247 		if (state.rollTimer > 0.55f)
5248 		{
5249 			state.rollTimer = 0;
5250 			if (dsq->continuity.form == FORM_DUAL)
5251 			{
5252 				switchDualFormMode();
5253 				state.rollTimer = -1;
5254 			}
5255 		}
5256 
5257 		// NOTE: does this fix the roll problem?
5258 		if (rollDelay <= 0)
5259 			stopRoll();
5260 	}
5261 
5262 	if (isActing(ACTION_ROLL))
5263 	{
5264 		if (_isUnderWater)
5265 		{
5266 			if (rollDelay < 0.5f)
5267 			{
5268 				startRoll(isfh()?1:-1);
5269 			}
5270 			float amt = dt * 1000;
5271 			if (isfh())
5272 			{
5273 				rotation.z += amt;
5274 			}
5275 			else
5276 			{
5277 				rotation.z -= amt;
5278 			}
5279 			rotation.capRotZ360();
5280 
5281 			rollDelay = 1.0;
5282 		}
5283 	}
5284 	else
5285 	{
5286 		int quad = getCursorQuadrant();
5287 		if (lastQuad != quad)
5288 		{
5289 			int quadDir = 0;
5290 			if (lastQuad != 0)
5291 			{
5292 				quadDir = getQuadrantDirection(lastQuad, quad);
5293 				if (quadDir != 0 && lastQuadDir == quadDir && rollDelay > 0)
5294 				{
5295 					if (rolling)
5296 					{
5297 						startRoll(quadDir);
5298 					}
5299 					else
5300 					{
5301 						if (rollDidOne==1)
5302 							rollDidOne = 2;
5303 						else if (rollDidOne == 2)
5304 							startRoll(quadDir);
5305 						else
5306 							rollDidOne = 1;
5307 					}
5308 				}
5309 			}
5310 
5311 			lastQuadDir = quadDir;
5312 
5313 			lastQuad = quad;
5314 
5315 			rollDelay = 0.2;
5316 		}
5317 	}
5318 }
5319 
getStopDistance()5320 int Avatar::getStopDistance()
5321 {
5322 	return STOP_DISTANCE;
5323 }
5324 
getBurstDistance()5325 int Avatar::getBurstDistance()
5326 {
5327 	return BURST_DISTANCE;
5328 }
5329 
setWasUnderWater()5330 void Avatar::setWasUnderWater()
5331 {
5332 	state.wasUnderWater = isUnderWater();
5333 }
5334 
canActivateStuff()5335 bool Avatar::canActivateStuff()
5336 {
5337 	return _canActivateStuff;
5338 }
5339 
setCanActivateStuff(bool on)5340 void Avatar::setCanActivateStuff(bool on)
5341 {
5342 	_canActivateStuff = on;
5343 }
5344 
setCollisionAvoidanceData(int range,float mod)5345 void Avatar::setCollisionAvoidanceData(int range, float mod)
5346 {
5347 	_collisionAvoidRange = range;
5348 	_collisionAvoidMod = mod;
5349 }
5350 
canQuickSong()5351 bool Avatar::canQuickSong()
5352 {
5353 	return !isSinging() && !isEntityDead() && isInputEnabled() && quickSongCastDelay <= 0;
5354 }
5355 
updateJoystick(float dt)5356 void Avatar::updateJoystick(float dt)
5357 {
5358 	if (canQuickSong())
5359 	{
5360 
5361 		if (core->joystick.dpadUp)
5362 		{
5363 			if (dsq->continuity.hasSong(SONG_ENERGYFORM) && dsq->continuity.form != FORM_ENERGY)
5364 			{
5365 				quickSongCastDelay = QUICK_SONG_CAST_DELAY;
5366 				dsq->continuity.castSong(SONG_ENERGYFORM);
5367 			}
5368 		}
5369 		else if (core->joystick.dpadDown && dsq->continuity.hasSong(SONG_BEASTFORM) && dsq->continuity.form != FORM_BEAST)
5370 		{
5371 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
5372 			dsq->continuity.castSong(SONG_BEASTFORM);
5373 		}
5374 		else if (core->joystick.dpadLeft && dsq->continuity.hasSong(SONG_SUNFORM) && dsq->continuity.form != FORM_SUN)
5375 		{
5376 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
5377 			dsq->continuity.castSong(SONG_SUNFORM);
5378 		}
5379 		else if (core->joystick.dpadRight && dsq->continuity.hasSong(SONG_NATUREFORM) && dsq->continuity.form != FORM_NATURE)
5380 		{
5381 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
5382 			dsq->continuity.castSong(SONG_NATUREFORM);
5383 		}
5384 
5385 	}
5386 }
5387 
applyRidingPosition()5388 void Avatar::applyRidingPosition()
5389 {
5390 	if (riding)
5391 	{
5392 		position = riding->getRidingPosition();
5393 		lastPosition = position;
5394 		rotation.z = riding->getRidingRotation();
5395 
5396 		if (riding->getRidingFlip())
5397 		{
5398 			if (!isfh())
5399 				flipHorizontal();
5400 		}
5401 		else
5402 		{
5403 			if (isfh())
5404 				flipHorizontal();
5405 		}
5406 		//state.wasUnderWater = _isUnderWater;
5407 	}
5408 }
5409 
adjustHeadRot()5410 void Avatar::adjustHeadRot()
5411 {
5412 	if (bone_head)
5413 	{
5414 		// 0 to 30 range
5415 		if (bone_head->rotation.z > 0)
5416 		{
5417 			bone_head->internalOffset.x = (bone_head->rotation.z/30.0f)*5;
5418 			//bone_head->internalOffset.y = (bone_head->rotation.z/30.0f)*1;
5419 		}
5420 		// 0 to -10 range
5421 		if (bone_head->rotation.z < 0)
5422 		{
5423 			bone_head->internalOffset.x = (bone_head->rotation.z/(-10.0f))*-4;
5424 			bone_head->internalOffset.y = (bone_head->rotation.z/(-10.0f))*-2;
5425 		}
5426 	}
5427 }
5428 
endOfGameState()5429 void Avatar::endOfGameState()
5430 {
5431 	state.lookAtEntity = 0;
5432 	setInvincible(true);
5433 }
5434 
5435 bool didRotationFix = true;
5436 
timerEffectStart(Timer * timer,ParticleEffect * effect)5437 void timerEffectStart(Timer *timer, ParticleEffect *effect)
5438 {
5439 	if (timer->isActive() && !effect->isRunning())
5440 	{
5441 		effect->start();
5442 	}
5443 	else if (!timer->isActive() && effect->isRunning())
5444 	{
5445 		effect->stop();
5446 	}
5447 }
5448 
updateFoodParticleEffects()5449 void Avatar::updateFoodParticleEffects()
5450 {
5451 	timerEffectStart(&dsq->continuity.speedMultTimer, &speedEmitter);
5452 	timerEffectStart(&dsq->continuity.defenseMultTimer, &defenseEmitter);
5453 	timerEffectStart(&dsq->continuity.invincibleTimer, &invincibleEmitter);
5454 	timerEffectStart(&dsq->continuity.regenTimer, &regenEmitter);
5455 }
5456 
updateLookAt(float dt)5457 void Avatar::updateLookAt(float dt)
5458 {
5459 	//if (dsq->overlay->alpha != 0) return;
5460 	if (dsq->game->isShuttingDownGameState()) return;
5461 	if (headTextureTimer > 0)
5462 	{
5463 		headTextureTimer -= dt;
5464 		if (headTextureTimer <= 0)
5465 		{
5466 			headTextureTimer = 0;
5467 			setHeadTexture("");
5468 		}
5469 	}
5470 
5471 	if (dsq->continuity.form == FORM_FISH)
5472 	{
5473 		Bone *b = skeletalSprite.getBoneByIdx(0);
5474 		if (b)
5475 			b->setAnimated(Bone::ANIM_ALL);
5476 		return;
5477 	}
5478 
5479 	const float blinkTime = 5.0;
5480 	state.blinkTimer += dt;
5481 	if (state.blinkTimer > blinkTime)
5482 	{
5483 		if (lastHeadTexture.empty())
5484 		{
5485 			setHeadTexture("blink", 0.1);
5486 			if (chance(50))
5487 			{
5488 				state.blinkTimer = blinkTime-0.2f;
5489 			}
5490 			else
5491 			{
5492 				state.blinkTimer = rand()%2;
5493 			}
5494 		}
5495 		else
5496 		{
5497 			state.blinkTimer -= dt;
5498 		}
5499 	}
5500 
5501 	if (bone_head)
5502 	{
5503 		const float lookAtTime = 0.8;
5504 		if (core->mouse.buttons.middle && !state.lockedToWall && isInputEnabled())
5505 		{
5506 			didRotationFix = false;
5507 			bone_head->setAnimated(Bone::ANIM_POS);
5508 			bone_head->lookAt(dsq->getGameCursorPosition(), lookAtTime, -10, 30, -90);
5509 			adjustHeadRot();
5510 		}
5511 		else
5512 		{
5513 			if (state.lookAtEntity && (state.lookAtEntity->isEntityDead() || state.lookAtEntity->isDead() || state.lookAtEntity->isv(EV_LOOKAT,0) || swimming))
5514 			{
5515 				state.lookAtEntity = 0;
5516 			}
5517 			// find an object of interest
5518 			if (isv(EV_LOOKAT, 1) && state.lookAtEntity && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->isAnimating() && !state.lockedToWall && !swimming)
5519 			{
5520 				didRotationFix = false;
5521 				bone_head->setAnimated(Bone::ANIM_POS);
5522 				bone_head->lookAt(state.lookAtEntity->getLookAtPoint(), lookAtTime, -10, 30, -90);
5523 
5524 				if (!((state.lookAtEntity->position - position).isLength2DIn(1000)))
5525 				{
5526 					state.lookAtEntity = 0;
5527 				}
5528 				state.updateLookAtTime += dt;
5529 				adjustHeadRot();
5530 			}
5531 			else
5532 			{
5533 				bone_head->setAnimated(Bone::ANIM_ALL);
5534 
5535 				if (!didRotationFix && !bone_head->rotationOffset.isInterpolating())
5536 				{
5537 					float t = 1;
5538 					didRotationFix = true;
5539 					float oldRot = bone_head->rotation.z;
5540 
5541 					skeletalSprite.updateBones();
5542 
5543 					bone_head->rotationOffset.z = oldRot - bone_head->rotation.z;
5544 					bone_head->rotationOffset.interpolateTo(Vector(0,0,0), t);
5545 					bone_head->internalOffset.interpolateTo(Vector(0,0,0), t);
5546 				}
5547 
5548 				state.updateLookAtTime += dt*4*2;
5549 				bone_head->internalOffset.interpolateTo(Vector(0,0), 0.2);
5550 			}
5551 
5552 			if (state.updateLookAtTime > 1.5f)
5553 			{
5554 				state.lookAtEntity = dsq->game->getNearestEntity(position, 800, this, ET_NOTYPE, DT_NONE, LR_ENTITIES0, LR_ENTITIES2);
5555 				if (state.lookAtEntity && state.lookAtEntity->isv(EV_LOOKAT, 1))
5556 				{
5557 					state.updateLookAtTime = 0;
5558 
5559 					if (!state.lookAtEntity->naijaReaction.empty())
5560 					{
5561 						setHeadTexture(state.lookAtEntity->naijaReaction, 1.5);
5562 					}
5563 				}
5564 				else
5565 				{
5566 					state.lookAtEntity = 0;
5567 
5568 				}
5569 			}
5570 		}
5571 	}
5572 }
5573 
getHeadPosition()5574 Vector Avatar::getHeadPosition()
5575 {
5576 	if (bone_head)
5577 		return bone_head->getWorldPosition();
5578 	return position;
5579 }
5580 
5581 bool lastCursorKeyboard = false;
5582 
onUpdate(float dt)5583 void Avatar::onUpdate(float dt)
5584 {
5585 	BBGE_PROF(Avatar_onUpdate);
5586 
5587 	looking = 0;
5588 
5589 #ifdef AQ_TEST_QUADTRAIL
5590 	quadTrail->addPoint(position);
5591 #endif
5592 
5593 	if (lightFormGlow)
5594 	{
5595 		if (dsq->continuity.light)
5596 		{
5597 			lightFormGlow->scale = Vector(6,6) + Vector(4,4)*dsq->continuity.light;
5598 		}
5599 		else
5600 		{
5601 			lightFormGlow->scale = Vector(6,6);
5602 		}
5603 	}
5604 
5605 	applyRidingPosition();
5606 	if (activateEntity)
5607 	{
5608 		activateEntity->activate();
5609 		activateEntity = 0;
5610 	}
5611 	if (bone_head)
5612 		headPosition = bone_head->getWorldPosition();
5613 
5614 	//vel /= 0;
5615 	if (vel.isNan())
5616 	{
5617 		debugLog("detected velocity NaN");
5618 		vel = Vector(0,0);
5619 	}
5620 
5621 	if (fireDelay > 0)
5622 	{
5623 		fireDelay -= dt;
5624 		if (fireDelay < 0)
5625 		{
5626 			fireDelay = 0;
5627 		}
5628 	}
5629 
5630 	if (isInputEnabled())
5631 	{
5632 		if (web)
5633 		{
5634 			if (!webBitTimer.isActive())
5635 			{
5636 				webBitTimer.start(0.5);
5637 			}
5638 			web->setPoint(curWebPoint, position);
5639 
5640 			if (webBitTimer.updateCheck(dt))
5641 			{
5642 				webBitTimer.start(0.5);
5643 
5644 				curWebPoint = web->addPoint(position);
5645 			}
5646 		}
5647 
5648 		if (!dsq->game->isPaused() && isActing(ACTION_LOOK) && !dsq->game->avatar->isSinging() && dsq->game->avatar->isInputEnabled() && !dsq->game->isInGameMenu())
5649 		{
5650 			looking = 1;
5651 		}
5652 		else
5653 		{
5654 			looking = 0;
5655 		}
5656 	}
5657 	else
5658 	{
5659 		looking = 0;
5660 	}
5661 
5662 
5663 	Entity::onUpdate(dt);
5664 
5665 	if (isEntityDead() && skeletalSprite.getCurrentAnimation()->name != "dead")
5666 	{
5667 		fallOffWall();
5668 		biteLeftEmitter.stop();
5669 		biteRightEmitter.stop();
5670 		wakeEmitter.stop();
5671 		rollLeftEmitter.stop();
5672 		rollRightEmitter.stop();
5673 		dsq->game->toggleOverrideZoom(false);
5674 		if (dsq->continuity.form != FORM_NORMAL)
5675 			changeForm(FORM_NORMAL);
5676 		setHeadTexture("Pain");
5677 		core->globalScale.interpolateTo(Vector(5,5),3);
5678 		rotation.interpolateTo(Vector(0,0,0), 0.1);
5679 		skeletalSprite.animate("dead");
5680 	}
5681 	if (isEntityDead())
5682 	{
5683 		dsq->game->toggleOverrideZoom(false);
5684 	}
5685 
5686 	if (dsq->user.control.targeting)
5687 		updateTargets(dt, false);
5688 	else
5689 		targets.clear();
5690 
5691 	updateTargetQuads(dt);
5692 
5693 	updateDualFormGlow(dt);
5694 	updateLookAt(dt);
5695 
5696 	updateFoodParticleEffects();
5697 
5698 	if (!dsq->game->isPaused())
5699 		myZoom.update(dt);
5700 
5701 	_isUnderWater = isUnderWater();
5702 
5703 	splashDelay -= dt;
5704 	if (splashDelay < 0)
5705 		splashDelay = 0;
5706 
5707 	// JUMPING OUT
5708 	if (!_isUnderWater && state.wasUnderWater)
5709 	{
5710 
5711 		// "falling" out, not bursting out
5712 		int fallOutSpeed = 200;
5713 		/*
5714 		if (waterBubble)
5715 			fallOutSpeed = 400;
5716 		*/
5717 		//bool waterBubbleRect = (waterBubble && waterBubble->pathShape == PATHSHAPE_RECT);
5718 
5719 		//&& !waterBubbleRect
5720 		if (!riding && (!bursting && vel.isLength2DIn(fallOutSpeed)))
5721 		{
5722 
5723 			if (waterBubble)
5724 			{
5725 				// prevent from falling out
5726 				// if circle, clamp
5727 				waterBubble->clampPosition(&position);
5728 				vel *= 0.5f;
5729 				startBurstCommon();
5730 			}
5731 			else
5732 			{
5733 				if (!dsq->game->waterLevel.isInterpolating())
5734 				{
5735 					if (vel.y < 0)
5736 						vel.y = -vel.y*0.5f;
5737 					position.y = dsq->game->waterLevel.x + collideRadius;
5738 				}
5739 			}
5740 		}
5741 		else
5742 		{
5743 			if (waterBubble)
5744 				lastJumpOutFromWaterBubble = true;
5745 			else
5746 				lastJumpOutFromWaterBubble = false;
5747 
5748 			lastWaterBubble = waterBubble;
5749 			waterBubble = 0;
5750 			BBGE_PROF(Avatar_splashOut);
5751 			splash(false);
5752 
5753 			if (dsq->continuity.form != FORM_FISH)
5754 			{
5755 				vel *= vars->jumpVelocityMod; // 1.25f;
5756 				vel.capLength2D(2000);
5757 				currentMaxSpeed *= 2.0f;
5758 			}
5759 			else
5760 			{
5761 				vel *= 1.5f;
5762 				vel.capLength2D(1500);
5763 				currentMaxSpeed *= 1.5f;
5764 			}
5765 
5766 			// total max speed
5767 			fallGravityTimer = 0.0;
5768 
5769 			wakeEmitter.stop();
5770 			biteTimer = 0;
5771 			//stopBurst();
5772 
5773 			// if first time
5774 			if (!dsq->mod.isActive() && dsq->continuity.getFlag("leftWater")==0 && dsq->game->sceneName.find("veil")!=std::string::npos)
5775 			{
5776 				setInvincible(true);
5777 				setv(EV_NOINPUTNOVEL, 0);
5778 
5779 				setWasUnderWater();
5780 
5781 				if (vel.y > -500)
5782 					vel.y = -500;
5783 				dsq->continuity.setFlag("leftWater", 1);
5784 
5785 				core->sound->fadeMusic(SFT_OUT, 2);
5786 				//("Veil");
5787 				dsq->game->avatar->disableInput();
5788 				dsq->gameSpeed.interpolateTo(0.1, 0.5);
5789 
5790 				//dsq->sound->setMusicFader(0.5, 0.5);
5791 				core->sound->playSfx("NaijaGasp");
5792 				core->main(0.75);
5793 
5794 
5795 
5796 				dsq->voiceOnce("Naija_VeilCrossing");
5797 				core->main(10*0.1f);
5798 
5799 				dsq->gameSpeed.interpolateTo(1, 0.2);
5800 
5801 				dsq->sound->playMusic("Veil", SLT_LOOP, SFT_CROSS, 20);
5802 
5803 				//dsq->sound->setMusicFader(1.0, 1);
5804 
5805 				dsq->game->avatar->enableInput();
5806 
5807 				setv(EV_NOINPUTNOVEL, 1);
5808 
5809 				setInvincible(false);
5810 
5811 				//dsq->continuity.setFlag("leftWater", 0);
5812 
5813 			}
5814 
5815 
5816 			state.outOfWaterTimer = 0;
5817 			state.outOfWaterVel = vel;
5818 			//startBackFlip();
5819 		}
5820 
5821 		if (currentMaxSpeed > dsq->v.maxOutOfWaterSpeed)
5822 		{
5823 			currentMaxSpeed = dsq->v.maxOutOfWaterSpeed;
5824 		}
5825 		if (currentMaxSpeed < 1200)
5826 		{
5827 			currentMaxSpeed = 1200;
5828 		}
5829 	}
5830 	// JUMPING IN
5831 	else if (_isUnderWater && !state.wasUnderWater)
5832 	{
5833 		// falling in
5834 		splash(true);
5835 
5836 		lastOutOfWaterMaxSpeed = getMaxSpeed();
5837 		lastOutOfWaterMaxSpeed *= 0.75f;
5838 
5839 		if (lastOutOfWaterMaxSpeed > 1000)
5840 			lastOutOfWaterMaxSpeed = 1000;
5841 
5842 		fallGravityTimer = 0.5;
5843 		if (rolling)
5844 			fallGravityTimer *= 1.5f;
5845 		stopBurst();
5846 
5847 		if (state.backFlip)
5848 		{
5849 			stopBackFlip();
5850 		}
5851 
5852 	}
5853 
5854 	state.wasUnderWater = _isUnderWater;
5855 
5856 	if (!_isUnderWater)
5857 	{
5858 		state.outOfWaterTimer += dt;
5859 		if (state.outOfWaterTimer > 100)
5860 			state.outOfWaterTimer = 100;
5861 	}
5862 
5863 
5864 	if (!state.backFlip && !_isUnderWater && state.outOfWaterTimer < 0.1f && !riding && !boneLock.on)
5865 	{
5866 		const int check = 64;
5867 		Vector m = getVectorToCursor();
5868 		if (state.outOfWaterVel.x < 0 && m.x > check)
5869 		{
5870 			startBackFlip();
5871 		}
5872 		if (state.outOfWaterVel.x > 0 && m.x < -check)
5873 		{
5874 			startBackFlip();
5875 		}
5876 	}
5877 
5878 	if (isEntityDead())
5879 	{
5880 		updateHair(dt);
5881 	}
5882 
5883 	if (isEntityDead()) return;
5884 
5885 	if (flourishTimer.updateCheck(dt))
5886 	{
5887 		flourish = 0;
5888 		rotationOffset.z = 0;
5889 	}
5890 
5891 	if (isInputEnabled())
5892 		stillTimer.update(dt);
5893 
5894 	if (vel.isZero()) //&& !isSinging())
5895 	{
5896 		if (!stillTimer.isActive())
5897 		{
5898 			stillTimer.startStopWatch();
5899 			//debugLog("start stillTimer");
5900 		}
5901 	}
5902 	else
5903 	{
5904 		stillTimer.stop();
5905 	}
5906 
5907 	flourishPowerTimer.updateCheck(dt);
5908 
5909 	if (isSinging())
5910 	{
5911 		if (songInterfaceTimer < 1)
5912 			songInterfaceTimer += dt;
5913 	}
5914 	updateJoystick(dt);
5915 
5916 	if (quickSongCastDelay>0)
5917 	{
5918 		quickSongCastDelay -= dt;
5919 		if (quickSongCastDelay < 0)
5920 			quickSongCastDelay = 0;
5921 	}
5922 	if (ripples && _isUnderWater)
5923 	{
5924 		rippleDelay -= dt;
5925 		if (rippleDelay < 0)
5926 		{
5927 			if (core->afterEffectManager)
5928 				core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.04,0.06,15,0.2f));
5929 			rippleDelay = 0.15;
5930 		}
5931 	}
5932 
5933 	if (dsq->continuity.tripTimer.isActive())
5934 	{
5935 		static int tripCount = 0;
5936 		tripDelay -= dt;
5937 		if (tripDelay < 0)
5938 		{
5939 			tripDelay = 0.15;
5940 			tripCount ++;
5941 			if (tripCount > 10)
5942 			{
5943 				/*
5944 				// hacktastic
5945 				EMOTE_NAIJAEVILLAUGH	= 0
5946 				EMOTE_NAIJAGIGGLE		= 1
5947 				EMOTE_NAIJALAUGH		= 2
5948 				EMOTE_NAIJASADSIGH		= 3
5949 				EMOTE_NAIJASIGH			= 4
5950 				EMOTE_NAIJAWOW			= 5
5951 				EMOTE_NAIJAUGH			= 6
5952 				*/
5953 				float p = dsq->continuity.tripTimer.getPerc();
5954 				if (p > 0.6f)
5955 				{
5956 					if (core->afterEffectManager)
5957 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.04,0.06,15,0.2f));
5958 				}
5959 				else
5960 				{
5961 					if (core->afterEffectManager)
5962 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.4,0.6,15,0.2f));
5963 				}
5964 				if (p > 0.75f){}
5965 				else if (p > 0.5f)
5966 				{
5967 					dsq->shakeCamera(2, 4);
5968 					if (chance(80))
5969 					{
5970 						if (chance(60))
5971 							dsq->emote.playSfx(2);
5972 						else
5973 							dsq->emote.playSfx(0);
5974 					}
5975 				}
5976 				else
5977 				{
5978 					if (p < 0.2f)
5979 						dsq->shakeCamera(10, 4);
5980 					else
5981 						dsq->shakeCamera(5, 4);
5982 					tripper->color.interpolateTo(Vector(1, 0.2, 0.2), 3);
5983 					if (chance(75))
5984 						dsq->emote.playSfx(6);
5985 				}
5986 
5987 				tripCount = 0;
5988 			}
5989 		}
5990 	}
5991 
5992 	if (position.isInterpolating())
5993 	{
5994 		lastPosition = position;
5995 	}
5996 
5997 	updateFormVisualEffects(dt);
5998 	updateRoll(dt);
5999 	updateWallJump(dt);
6000 
6001 	if (formAbilityDelay > 0)
6002 	{
6003 		formAbilityDelay -= dt;
6004 		if (formAbilityDelay < 0)
6005 			formAbilityDelay = 0;
6006 	}
6007 	//updateCursor(dt);
6008 
6009 	if (getState() == STATE_PUSH)
6010 	{
6011 		/*
6012 		if (rotation.z < 0)
6013 			rotation.z += 360;
6014 		if (rotation.z > 360)
6015 			rotation.z -= 360;
6016 		*/
6017 		rotateToVec(vel, 0, -90);
6018 		if (vel.x < 0&& !isfh())
6019 			flipHorizontal();
6020 		else if (vel.x > 0 && isfh())
6021 			flipHorizontal();
6022 	}
6023 
6024 	updateAura(dt);
6025 
6026 	updateSingingInterface(dt);
6027 
6028 	if (pullTarget)
6029 	{
6030 		if (pullTarget->life < 1 || !pullTarget->isPullable())
6031 		{
6032 			pullTarget->stopPull();
6033 			pullTarget = 0;
6034 		}
6035 	}
6036 
6037 	formTimer += dt;
6038 
6039 
6040 	if (dsq->continuity.form == FORM_SPIRIT)
6041 	{
6042 		if (useSpiritDistance)
6043 		{
6044 			if (formTimer > 1)
6045 			{
6046 				if (!(bodyPosition - position).isLength2DIn(SPIRIT_RANGE))
6047 				{
6048 					changeForm(FORM_NORMAL);
6049 				}
6050 			}
6051 		}
6052 		// here
6053 		if (!_isUnderWater)
6054 		{
6055 			changeForm(FORM_NORMAL);
6056 		}
6057 	}
6058 
6059 	// revert stuff
6060 	float revertGrace = 0.4f;
6061 	static bool revertButtonsAreDown = false;
6062 	if (inputEnabled && (dsq->inputMode == INPUT_KEYBOARD || dsq->inputMode == INPUT_MOUSE) && (!pathToActivate && !entityToActivate))
6063 	{
6064 		if (dsq->continuity.form != FORM_NORMAL && (core->mouse.pure_buttons.left && core->mouse.pure_buttons.right) && getVectorToCursor(true).isLength2DIn(minMouse))
6065 		{
6066 			if (!revertButtonsAreDown)
6067 			{
6068 				revertTimer = revertGrace;
6069 				revertButtonsAreDown = true;
6070 			}
6071 			else if (revertButtonsAreDown)
6072 			{
6073 				if (revertTimer > 0)
6074 				{
6075 					revertTimer -= dt;
6076 					if (revertTimer < 0)
6077 					{
6078 						revertTimer = 0;
6079 					}
6080 				}
6081 			}
6082 		}
6083 		//&& !isActing(ACTION_PRIMARY) && !isActing(ACTION_SECONDARY)
6084 		else if ((!core->mouse.pure_buttons.left && !core->mouse.pure_buttons.right))
6085 		{
6086 			if (revertTimer > 0 && getVectorToCursor(true).isLength2DIn(minMouse) && state.spellCharge < revertGrace+0.1f)
6087 			{
6088 				revert();
6089 				//changeForm(FORM_NORMAL);
6090 			}
6091 			revertButtonsAreDown = false;
6092 			revertTimer = 0;
6093 		}
6094 	}
6095 	else
6096 	{
6097 		revertButtonsAreDown = false;
6098 	}
6099 
6100 	//if (core->getNestedMains() == 1)
6101 	{
6102 		if (getState() != STATE_TRANSFORM && !dsq->game->isWorldPaused())
6103 		{
6104 			formAbilityUpdate(dt);
6105 		}
6106 
6107 		if (state.useItemDelay.updateCheck(dt))
6108 		{
6109 		}
6110 
6111 		ActionMapper::onUpdate(dt);
6112 
6113 		if (inputEnabled)
6114 		{
6115 			if (state.blind)
6116 			{
6117 				if (state.blindTimer.updateCheck(dt))
6118 				{
6119 					state.blind = false;
6120 					removeBlindEffects();
6121 				}
6122 			}
6123 		}
6124 
6125 
6126 		if (boneLock.entity != 0)
6127 		{
6128 			/*
6129 			std::ostringstream os;
6130 			os << "boneLock.wallNormal(" << boneLock.wallNormal.x << ", " << boneLock.wallNormal.y << ")";
6131 			debugLog(os.str());
6132 			*/
6133 			if (!_isUnderWater && !(boneLock.wallNormal.y < -0.03f))
6134 			{
6135 				if (lockToWallFallTimer == -1)
6136 					lockToWallFallTimer = 0.4;
6137 			}
6138 			else
6139 				lockToWallFallTimer = -1;
6140 		}
6141 
6142 		if (lockToWallFallTimer > 0)
6143 		{
6144 			lockToWallFallTimer -= dt;
6145 			if (lockToWallFallTimer <= 0)
6146 			{
6147 				fallOffWall();
6148 			}
6149 		}
6150 
6151 		if (state.lockToWallDelay.updateCheck(dt))
6152 		{
6153 		}
6154 
6155 		if (pushingOffWallEffect > 0)
6156 		{
6157 			pushingOffWallEffect -= dt;
6158 			if (pushingOffWallEffect <= 0)
6159 			{
6160 				pushingOffWallEffect = 0;
6161 				if (vel.getSquaredLength2D() > sqr(1200))
6162 				{
6163 					vel.setLength2D(1200);
6164 				}
6165 			}
6166 		}
6167 
6168 		if (charging)
6169 		{
6170 			state.spellCharge += dt;
6171 			switch (dsq->continuity.form)
6172 			{
6173 			case FORM_SUN:
6174 			{
6175 				if (state.spellCharge > 1.5f && chargeLevelAttained <1)
6176 				{
6177 					chargeLevelAttained = 1.5;
6178 					core->sound->playSfx("PowerUp");
6179 					chargingEmitter->load("ChargingEnergy2");
6180 				}
6181 			}
6182 			break;
6183 			case FORM_DUAL:
6184 			{
6185 				if (state.spellCharge >= 1.4f && chargeLevelAttained<1)
6186 				{
6187 					chargeLevelAttained = 1;
6188 
6189 					core->sound->playSfx("PowerUp");
6190 					//debugLog("charge visual effect 2");
6191 					chargeEmitter->load("ChargeDualForm");
6192 					chargeEmitter->start();
6193 
6194 					chargingEmitter->load("ChargedDualForm");
6195 					chargingEmitter->start();
6196 				}
6197 			}
6198 			break;
6199 			case FORM_ENERGY:
6200 			{
6201 				if (state.spellCharge >= 1.5f && chargeLevelAttained<2)
6202 				{
6203 					chargeLevelAttained = 2;
6204 					core->sound->playSfx("PowerUp");
6205 					//debugLog("charge visual effect 2");
6206 					chargeEmitter->load("ChargeEnergy");
6207 					chargeEmitter->start();
6208 
6209 
6210 					chargingEmitter->load("ChargingEnergy2");
6211 					//chargeVisualEffect("particles/energy-charge-2");
6212 				}
6213 			}
6214 			break;
6215 			case FORM_NATURE:
6216 			{
6217 				if (state.spellCharge >= 0.9f && chargeLevelAttained<2)
6218 				{
6219 					chargeLevelAttained = 2;
6220 					core->sound->playSfx("PowerUp");
6221 					chargeEmitter->load("ChargeNature2");
6222 					chargeEmitter->start();
6223 
6224 					chargingEmitter->load("ChargingNature2");
6225 					chargingEmitter->start();
6226 				}
6227 			}
6228 			break;
6229 			}
6230 		}
6231 		/*
6232 		float angle = PI - ((rotation.z/180)*PI);
6233 		int height = 25;
6234 		*/
6235 		//hair->hairNodes[0].position = position + Vector(sinf(angle)*height, cosf(angle)*height);
6236 
6237 
6238 		if (biteTimer < biteTimerMax)
6239 		{
6240 			biteTimer += dt;
6241 		}
6242 		else
6243 		{
6244 			biteLeftEmitter.stop();
6245 			biteRightEmitter.stop();
6246 			biteTimer = biteTimerMax;
6247 		}
6248 
6249 		if (biteTimer > biteTimerBiteRange)
6250 		{
6251 			biteLeftEmitter.stop();
6252 			biteRightEmitter.stop();
6253 		}
6254 
6255 		/*
6256 		std::ostringstream os;
6257 		os << "biteTimer: " << biteTimer;
6258 		debugLog(os.str());
6259 		*/
6260 
6261 		if (isInputEnabled())
6262 		{
6263 			if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "urchin") == 0)
6264 			{
6265 				if (!isEntityDead() && health > 0)
6266 				{
6267 					urchinDelay -= dt;
6268 					if (urchinDelay < 0)
6269 					{
6270 						urchinDelay = 0.1;
6271 
6272 						dsq->game->fireShot("urchin", this, 0, position + offset);
6273 					}
6274 				}
6275 			}
6276 
6277 			if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "jelly")==0)
6278 			{
6279 				if (!isEntityDead() && health > 0)
6280 				{
6281 					if (health < (maxHealth*JELLYCOSTUME_HEALTHPERC))
6282 					{
6283 						jellyDelay -= dt;
6284 						if (jellyDelay < 0)
6285 						{
6286 							jellyDelay = JELLYCOSTUME_HEALDELAY;
6287 
6288 							Vector d;
6289 							if (!vel.isZero())
6290 							{
6291 								d = vel;
6292 								d.setLength2D(16);
6293 							}
6294 
6295 							dsq->game->spawnManaBall(position + offset + d, JELLYCOSTUME_HEALAMOUNT);
6296 
6297 							//Shot *s = dsq->game->fireShot("urchin", this, 0, getWorldPosition());
6298 						}
6299 					}
6300 				}
6301 			}
6302 		}
6303 
6304 		if (dsq->continuity.form == FORM_BEAST && bone_head && biteTimer < biteTimerBiteRange && biteTimer > 0)
6305 		{
6306 			biteDelay -= dt;
6307 			if (biteDelay < 0)
6308 			{
6309 				biteDelay = biteDelayPeriod;
6310 
6311 				Vector p = bone_head->getWorldPosition();
6312 				std::string shot = "Bite";
6313 				if (dsq->continuity.biteMult > 1)
6314 				{
6315 					shot = "SuperBite";
6316 				}
6317 				dsq->game->fireShot(shot, this, 0, p);
6318 				//s->setAimVector(getNormal());
6319 			}
6320 		}
6321 
6322 		if (dsq->continuity.form == FORM_FISH && dsq->continuity.fishPoisonTimer.isActive())
6323 		{
6324 			if (!vel.isLength2DIn(16))
6325 			{
6326 				static float fishPoison = 0;
6327 				fishPoison += dt;
6328 				if (fishPoison > 0.2f)
6329 				{
6330 					fishPoison = 0;
6331 					dsq->game->fireShot("FishPoison", this, 0, position);
6332 				}
6333 			}
6334 		}
6335 
6336 		if (!state.lockedToWall && _isUnderWater && !dsq->game->isWorldPaused() && canMove)
6337 		{
6338 			if (bursting)
6339 			{
6340 				//debugLog("bursting~!");
6341 				burst -= dt * BURST_USE_RATE;
6342 				burstTimer += dt;
6343 
6344 				/*
6345 				std::ostringstream os;
6346 				os << "burst: " << burst;
6347 				debugLog(os.str());
6348 				*/
6349 				if (burst <= 0)
6350 				{
6351 					stopBurst();
6352 				}
6353 			}
6354 
6355 
6356 		}
6357 		if (inputEnabled && _isUnderWater)
6358 		{
6359 			if(bursting)
6360 			{
6361 			}
6362 			else if (burstDelay > 0)
6363 			{
6364 				burstDelay -= dt;
6365 				if (burstDelay <= 0)
6366 					burstDelay = 0;
6367 			}
6368 			else if (burst < 1)
6369 			{
6370 				burst += BURST_RECOVER_RATE * dt;
6371 				if (burst >= 1)
6372 					burst = 1;
6373 			}
6374 		}
6375 
6376 		bool moved = false;
6377 
6378 		//check to make sure there's still a wall there, if not fall off
6379 		if (state.lockedToWall)
6380 		{
6381 			rotateToVec(wallPushVec, dt*2);
6382 			if (!boneLock.on && !dsq->game->isObstructed(wallLockTile))
6383 			{
6384 				//debugLog("Dropping from wall");
6385 				fallOffWall();
6386 			}
6387 		}
6388 
6389 		if (getState() != STATE_PUSH && !state.lockedToWall && inputEnabled && _isUnderWater && canMove)
6390 		{
6391 			float a = 800*dt;
6392 			Vector addVec;
6393 
6394 			bool isMovingSlow = false;
6395 
6396 			float len = 0;
6397 
6398 			if (dsq->isMiniMapCursorOkay() && !isActing(ACTION_ROLL) &&
6399 				_isUnderWater && !riding && !boneLock.on &&
6400 				(movingOn || ((dsq->inputMode == INPUT_JOYSTICK || dsq->inputMode== INPUT_KEYBOARD) || (core->mouse.buttons.left || bursting))))
6401 			{
6402 				if (dsq->inputMode == INPUT_MOUSE || !this->singing)
6403 				{
6404 					addVec = getVectorToCursorFromScreenCentre();//getVectorToCursor();
6405 
6406 					if (dsq->inputMode == INPUT_MOUSE)
6407 					{
6408 						static Vector lastAddVec;
6409 						if (!isActing(ACTION_PRIMARY) && bursting)
6410 						{
6411 							addVec = lastAddVec;
6412 						}
6413 
6414 						if (bursting)
6415 						{
6416 							lastAddVec = addVec;
6417 						}
6418 					}
6419 
6420 					if (addVec.isLength2DIn(minMouse))
6421 					{
6422 						if (dsq->inputMode == INPUT_JOYSTICK)
6423 							addVec = Vector(0,0,0);
6424 					}
6425 
6426 					if (!addVec.isLength2DIn(minMouse))
6427 					{
6428 						//if (core->mouse.buttons.left)
6429 						{
6430 							len = addVec.getLength2D();
6431 							if (len > 100)
6432 								addVec.setLength2D(a *2);
6433 							else
6434 								addVec.setLength2D(a);
6435 
6436 							addVec *= dsq->continuity.speedMult;
6437 
6438 							//128
6439 							if (len < maxMouse && !bursting)
6440 							{
6441 								isMovingSlow = true;
6442 							}
6443 						}
6444 					}
6445 					else
6446 					{
6447 						// stop movement
6448 						// For joystick/keyboard control, don't stop unless
6449 						// the Swim (primary action) button is pressed with
6450 						// no movement input.  --achurch
6451 						if ((dsq->inputMode == INPUT_MOUSE || isActing(ACTION_PRIMARY))
6452 							&& addVec.isLength2DIn(STOP_DISTANCE))
6453 						{
6454 							vel *= 0.9f;
6455 							if (!rolling)
6456 								rotation.interpolateTo(Vector(0,0,0), 0.1);
6457 							if (vel.isLength2DIn(50))
6458 							{
6459 								if (bursting)
6460 								{
6461 									stopBurst();
6462 								}
6463 							}
6464 						}
6465 						addVec = Vector(0,0,0);
6466 					}
6467 				}
6468 			}
6469 
6470 			if (!rolling && !state.backFlip && !flourish)
6471 			{
6472 				if (addVec.x > 0)
6473 				{
6474 					if (!isfh())
6475 						flipHorizontal();
6476 				}
6477 				if (addVec.x < 0)
6478 				{
6479 					if (isfh())
6480 						flipHorizontal();
6481 				}
6482 			}
6483 
6484 			// will not get here if not underwater
6485 			if (isLockable())
6486 				lockToWall();
6487 			if ((addVec.x != 0 || addVec.y != 0))
6488 			{
6489 				currentMaxSpeed=0;
6490 				vel += addVec;
6491 
6492 				if (bursting)
6493 				{
6494 					Vector add = addVec;
6495 					add.setLength2D(BURST_ACCEL*dt);
6496 					vel += add;
6497 
6498 					if (pushingOffWallEffect > 0)
6499 						currentMaxSpeed = vars->maxWallJumpBurstSpeed;
6500 					else
6501 						currentMaxSpeed = vars->maxBurstSpeed;
6502 
6503 				}
6504 				else
6505 				{
6506 					if (pushingOffWallEffect > 0)
6507 						currentMaxSpeed = vars->maxWallJumpSpeed;
6508 					else
6509 					{
6510 						if (isActing(ACTION_SLOW) || isMovingSlow)
6511 						{
6512 							currentMaxSpeed = vars->maxSlowSwimSpeed;
6513 						}
6514 						else
6515 							currentMaxSpeed = vars->maxSwimSpeed;
6516 					}
6517 				}
6518 
6519 				if (leaches > 0)
6520 				{
6521 					currentMaxSpeed -= leaches*60;
6522 				}
6523 
6524 				if (state.blind)
6525 					currentMaxSpeed -= 100;
6526 
6527 				if (currentMaxSpeed < 0)
6528 					currentMaxSpeed = 1;
6529 
6530 				if (getState() == STATE_TRANSFORM)
6531 					rotateToVec(addVec, 0.1, 90);
6532 				else
6533 				{
6534 					if (rolling)
6535 					{
6536 						// here for roll key?
6537 						// seems like this isn't reached
6538 						//if (isActing("roll"))
6539 						if (isActing(ACTION_ROLL))
6540 						{
6541 							//debugLog("here");
6542 						}
6543 						else
6544 						{
6545 							float t = 0;
6546 							if (dsq->inputMode == INPUT_KEYBOARD)
6547 								t = 0.1;
6548 							rotateToVec(addVec, t);
6549 						}
6550 					}
6551 					else if (bursting && flourish)
6552 					{
6553 
6554 					}
6555 					else
6556 					{
6557 						if (!state.nearWall && !flourish)
6558 							rotateToVec(addVec, 0.1);
6559 					}
6560 				}
6561 
6562 				moved = true;
6563 				if ((!swimming || (swimming && !bursting && skeletalSprite.getCurrentAnimation()->name != "swim")) && !state.lockedToWall)
6564 				{
6565 					swimming = true;
6566 					//Animation *a = skeletalSprite.getCurrentAnimation();
6567 
6568 					if (getState() == STATE_IDLE && !rolling)
6569 					{
6570 						skeletalSprite.transitionAnimate("swim", ANIM_TRANSITION, -1);
6571 					}
6572 						//animate(anim_swim);
6573 				}
6574 				skeletalSprite.setTimeMultiplier(1);
6575 				Animation *anim=skeletalSprite.getCurrentAnimation();
6576 				if (!bursting && (anim && anim->name == "swim"))
6577 				{
6578 					float velLen = vel.getLength2D();
6579 					float time = velLen / 1200.0f;
6580 					if (velLen > 1200)
6581 						time = 1;
6582 					//skeletalSprite.setTimeMultiplier(time*3);// 5
6583 					//skeletalSprite.setTimeMultiplier(time*3.5f);
6584 					//skeletalSprite.setTimeMultiplier(time*4);
6585 					//animator.timePeriod = 1.5f*(1.0f-time);
6586 					skeletalSprite.setTimeMultiplier(time*4.5f);
6587 				}
6588 				else
6589 				{
6590 					if (currentAnim != getBurstAnimName() && skeletalSprite.getCurrentAnimation()->name != getBurstAnimName() && !state.lockedToWall)
6591 					{
6592 						if (getState() == STATE_IDLE && !rolling)
6593 							skeletalSprite.transitionAnimate(getBurstAnimName(), ANIM_TRANSITION);
6594 					}
6595 				}
6596 			}
6597 		}
6598 
6599 		//int currentSwimSpeed = 400;
6600 		//if (dsq->continuity.getWorldType() == WT_SPIRIT)
6601 		/*
6602 		if (dsq->continuity.form == FORM_SPIRIT)
6603 		{
6604 			currentSwimSpeed *= 0.3f;
6605 		}
6606 		*/
6607 		if (!_isUnderWater && !state.lockedToWall)
6608 		{
6609 			//currentSwimSpeed *= 1.5f;
6610 			//float a = *dt;
6611 			// base on where the mouse is
6612 			/*
6613 			Vector addVec;
6614 			addVec = getVectorToCursorFromScreenCentre();
6615 			addVec.setLength2D(a);
6616 			*/
6617 
6618 			// gravity
6619 			float fallMod = 1.5;
6620 			if (dsq->continuity.form == FORM_SPIRIT)
6621 			{
6622 				fallMod = 1.0;
6623 			}
6624 			vel += Vector(0,980)*dt*fallMod;
6625 
6626 			if (!rolling && !state.backFlip && !flourish)
6627 			{
6628 				if (vel.x != 0 || vel.y != 0)
6629 					rotateToVec(vel, 0.1);
6630 
6631 				if (vel.x > 0)
6632 				{
6633 					if (!isfh())
6634 						flipHorizontal();
6635 				}
6636 				if (vel.x < 0)
6637 				{
6638 					if (isfh())
6639 						flipHorizontal();
6640 				}
6641 			}
6642 			if (rolling && !state.backFlip)
6643 			{
6644 				Vector v = getVectorToCursorFromScreenCentre();
6645 				rotateToVec(v, 0.01);
6646 			}
6647 			if (isLockable())
6648 				lockToWall();
6649 		}
6650 
6651 		if (!moved)
6652 		{
6653 			if (swimming)
6654 			{
6655 				swimming = false;
6656 				if (dsq->continuity.form == FORM_FISH)
6657 					rotation.interpolateTo(0, 0.2);
6658 			}
6659 			// "friction"
6660 			//vel += -vel*0.999f*dt;
6661 			if (_isUnderWater)
6662 			{
6663 				/*
6664 				std::ostringstream os;
6665 				os << "fric(" << vel.x << ", " << vel.y;
6666 				debugLog(os.str());
6667 				*/
6668 				if (isInCurrent())
6669 					doFriction(dt*5);
6670 				else
6671 					doFriction(dt);
6672 			}
6673 		}
6674 
6675 	}
6676 
6677 	if (_isUnderWater && isInCurrent())
6678 	{
6679 		if (dsq->loops.current == BBGE_AUDIO_NOCHANNEL)
6680 		{
6681 			PlaySfx play;
6682 			play.name = "CurrentLoop";
6683 			play.vol = 1;
6684 			play.time = 1;
6685 			play.fade = SFT_IN;
6686 			play.loops = -1;
6687 			dsq->loops.current = core->sound->playSfx(play);
6688 		}
6689 	}
6690 	else
6691 	{
6692 		if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
6693 		{
6694 			core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
6695 			dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
6696 		}
6697 	}
6698 
6699 	if (!swimming && _isUnderWater)
6700 	{
6701 		if (!inCurrent)
6702 			currentMaxSpeed = vars->maxSwimSpeed;
6703 
6704 		if (!state.lockedToWall && !bursting)
6705 		{
6706 			if (getState() == STATE_IDLE && inputEnabled)
6707 			{
6708 				Animation *a = skeletalSprite.getCurrentAnimation(0);
6709 				if (a && a->name != getIdleAnimName() && a->name != "pushed" && a->name != "spin" && !rolling)
6710 					skeletalSprite.transitionAnimate(getIdleAnimName(), ANIM_TRANSITION, -1);
6711 			}
6712 		}
6713 	}
6714 
6715 	if (_isUnderWater && fallGravityTimer)
6716 	{
6717 		fallGravityTimer -= dt;
6718 		currentMaxSpeed = lastOutOfWaterMaxSpeed;
6719 		if (fallGravityTimer < 0)
6720 			fallGravityTimer = 0;
6721 	}
6722 
6723 
6724 
6725 	clampVelocity();
6726 
6727 	if (swimming)
6728 	{
6729 		if (!rolling && !internalOffset.isInterpolating())
6730 		{
6731 			int spread = 8;
6732 			//int rotSpread = 45;
6733 			float t = 1;
6734 
6735 			internalOffset = Vector(-spread, 0);
6736 			internalOffset.interpolateTo(Vector(spread, 0), t, -1, 1, 1);
6737 
6738 			for (int i = 0; i < int((t*0.5f)/0.01f); i++)
6739 			{
6740 				internalOffset.update(0.01);
6741 			}
6742 		}
6743 
6744 		if (dsq->continuity.form != FORM_ENERGY && dsq->continuity.form != FORM_DUAL && dsq->continuity.form != FORM_FISH)
6745 		{
6746 			if (leaches <= 0 && !bursting && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
6747 			{
6748 				state.swimTimer += dt;
6749 
6750 				if (state.swimTimer > 5)
6751 				{
6752 					state.swimTimer = 0 - rand()%3;
6753 					static int lastSwimExtra = -1;
6754 					int maxAnim = 4;
6755 					int anim = rand()%maxAnim;
6756 					if (anim == lastSwimExtra)
6757 						anim++;
6758 					if (anim >= maxAnim)
6759 						anim = 0;
6760 					lastSwimExtra = anim;
6761 					anim ++;
6762 
6763 					std::ostringstream os;
6764 					os << "swimExtra-" << anim;
6765 					skeletalSprite.transitionAnimate(os.str(), 0.5, 0, ANIMLAYER_UPPERBODYIDLE);
6766 				}
6767 			}
6768 		}
6769 	}
6770 	else
6771 	{
6772 
6773 	}
6774 
6775 	if (!swimming || rolling)
6776 	{
6777 		//state.swimTimer = 0;
6778 		if (skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
6779 		{
6780 			skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
6781 		}
6782 		internalOffset.interpolateTo(Vector(0,0),0.5);
6783 	}
6784 
6785 	checkNearWall();
6786 	//if (core->getNestedMains()==1)
6787 	{
6788 		Vector zoomSurface(0.55, 0.55);
6789 		Vector zoomMove(vars->zoomMove, vars->zoomMove), zoomStop(vars->zoomStop, vars->zoomStop), zoomNaija(vars->zoomNaija, vars->zoomNaija);
6790 		float cheatLen = getMoveVel().getSquaredLength2D();
6791 
6792 		if (cheatLen > sqr(250) && _isUnderWater && !state.lockedToWall)
6793 		{
6794 			if (!swimEmitter.isRunning())
6795 				swimEmitter.start();
6796 		}
6797 		else
6798 		{
6799 			swimEmitter.stop();
6800 		}
6801 
6802 		Vector targetScale(1,1);
6803 		if (zoomOverriden || isEntityDead() || core->globalScale.isInterpolating())
6804 		{
6805 
6806 		}
6807 		else
6808 		{
6809 			if (dsq->game->waterLevel.x > 0 && fabsf(avatar->position.y - dsq->game->waterLevel.x) < 800)
6810 			{
6811 				float time = 0.5;
6812 				if (!myZoom.isInterpolating() || ((!core->globalScale.data || core->globalScale.data->target != zoomSurface) && myZoom.data->timePeriod != time))
6813 				{
6814 					myZoom.interpolateTo(zoomSurface, time, 0, 0, 1);
6815 				}
6816 			}
6817 			else if (avatar->looking == 2)
6818 			{
6819 				float time = 1.0;
6820 				if (!myZoom.isInterpolating() || ((!core->globalScale.data || core->globalScale.data->target != zoomNaija) && myZoom.data->timePeriod != time))
6821 				{
6822 					/*
6823 					std::ostringstream os;
6824 					os << "zooming in on Naija: " << zoomNaija.x;
6825 					debugLog(os.str());
6826 					*/
6827 					myZoom.interpolateTo(zoomNaija, time, 0, 0, 1);
6828 				}
6829 			}
6830 			else if ((cheatLen > sqr(250) && cheatLen < sqr(1000)) || attachedTo || avatar->looking==1)
6831 			{
6832 				float time = 3;
6833 				if (avatar->looking)
6834 				{
6835 					time = 1.0;
6836 				}
6837 				if (!myZoom.isInterpolating() || ((!core->globalScale.data || core->globalScale.data->target != zoomMove) && myZoom.data->timePeriod != time))
6838 					myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
6839 			}
6840 			else if (cheatLen < sqr(210) && !state.lockedToWall && stillTimer.getValue() > 4 && !isSinging())
6841 			{
6842 				float time = 10;
6843 				if (!myZoom.isInterpolating() || (myZoom.data->target != zoomStop && myZoom.data->timePeriod != time))
6844 					myZoom.interpolateTo(zoomStop, time, 0, 0, 1);
6845 			}
6846 			else if (cheatLen >= sqr(1000))
6847 			{
6848 				float time = 1.6;
6849 				if (!myZoom.isInterpolating() || (myZoom.data->target != zoomMove && myZoom.data->timePeriod != time))
6850 					myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
6851 			}
6852 
6853 			if (myZoom.x < game->maxZoom)
6854 			{
6855 				core->globalScale.x = game->maxZoom;
6856 				core->globalScale.y = game->maxZoom;
6857 			}
6858 			else
6859 			{
6860 				core->globalScale.x = myZoom.x;
6861 				core->globalScale.y = myZoom.y;
6862 			}
6863 			core->globalScaleChanged();
6864 
6865 		}
6866 
6867 		if (!state.lockedToWall && !bursting && _isUnderWater && swimming && !isFollowingPath() && _collisionAvoidRange > 0)
6868 		{
6869 			doCollisionAvoidance(dt, _collisionAvoidRange, _collisionAvoidMod, 0, 800, OT_HURT);
6870 		}
6871 
6872 		if (!game->isShuttingDownGameState())
6873 		{
6874 			updateCurrents(dt);
6875 			updateVel2(dt);
6876 		}
6877 		else
6878 		{
6879 			vel2 = Vector(0,0,0);
6880 		}
6881 
6882 		if (!state.lockedToWall && !isFollowingPath() && !riding)
6883 		{
6884 /*collideCheck:*/
6885 
6886 			// Beware: This code may cause clamping vel to zero if the framerate is very high.
6887 			// Starting with zero vel, low difftimes will cause an addVec small enough that this
6888 			// check will always trigger, and vel will never get larger than zero.
6889 			// Under water and swimming check should hopefully prevent this from happening. -- FG
6890 			if (_isUnderWater && !isSwimming() && vel.getLength2D() < sqr(2))
6891 			{
6892 				vel = Vector(0,0,0);
6893 			}
6894 			Vector moveVel;
6895 			if (!isInputEnabled() && isv(EV_NOINPUTNOVEL, 1))
6896 			{
6897 				vel2=vel=Vector(0,0);
6898 			}
6899 			else
6900 			{
6901 				moveVel = getMoveVel();
6902 			}
6903 			if (!moveVel.isZero())
6904 			{
6905 				bool collided = false;
6906 
6907 				/*
6908 				std::ostringstream os;
6909 				os << "vel (" << vel.x << ", " << vel.y << ")";
6910 				debugLog(os.str());
6911 				*/
6912 				Vector mov = (moveVel * dt);
6913 				Vector omov = mov;
6914 				mov.capLength2D(TILE_SIZE);
6915 				/*
6916 				if (mov.getSquaredLength2D() > sqr(TILE_SIZE))
6917 					mov.setLength2D(TILE_SIZE);
6918 				*/
6919 				if (omov.getSquaredLength2D() > 0)
6920 				{
6921 					while (omov.getSquaredLength2D() > 0)
6922 					{
6923 						if (omov.getSquaredLength2D() < sqr(TILE_SIZE))
6924 						{
6925 							mov = omov;
6926 							omov = Vector(0,0);
6927 						}
6928 						else
6929 							omov -= mov;
6930 
6931 						lastPosition = position;
6932 						Vector newPosition = position + mov;
6933 						//Vector testPosition = position + (vel *dt)*2;
6934 						position = newPosition;
6935 
6936 						if (dsq->game->collideCircleWithGrid(position, collideRadius))
6937 						{
6938 							if (dsq->game->lastCollideTileType == OT_HURT
6939 								&& isDamageTarget(DT_WALLHURT))
6940 							{
6941 								DamageData d;
6942 								d.damage = 1;
6943 								d.damageType = DT_WALLHURT;
6944 								damage(d);
6945 								vel2 = Vector(0,0,0);
6946 								//doCollisionAvoidance(1, 3, 1);
6947 								/*
6948 								Vector v = dsq->game->getWallNormal(position);
6949 								if (!v.isZero())
6950 								{
6951 									vel += v * 500;
6952 								}
6953 								*/
6954 							}
6955 							collided = true;
6956 
6957 							if (currentState == STATE_PUSH)
6958 							{
6959 								dsq->sound->playSfx("rockhit");
6960 								dsq->spawnParticleEffect("rockhit", position);
6961 								if (pushDamage)
6962 								{
6963 									DamageData d;
6964 									d.damage = pushDamage;
6965 									damage(d);
6966 								}
6967 								setState(STATE_IDLE);
6968 							}
6969 
6970 							if (!_isUnderWater)
6971 							{
6972 								/*
6973 								doBounce();
6974 								position = lastPosition;
6975 								*/
6976 
6977 								// this is the out of water bounce...
6978 
6979 								//debugLog("above water bounce");
6980 
6981 
6982 
6983 								Vector n = getWallNormal(TileVector(lastPosition));
6984 								n *= vel.getLength2D();
6985 
6986 								/*
6987 								std::ostringstream os;
6988 								os << "vel(" << vel.x << ", " << vel.y << ") n(" << n.x << ", " << n.y << ")";
6989 								debugLog(os.str());
6990 								*/
6991 
6992 								//flopping = true;
6993 
6994 								vel = -vel;
6995 
6996 								//vel = (vel*0.5f + n*0.5f);
6997 								if (vel.y < 0)
6998 								{
6999 									//debugLog("Vel less than 0");
7000 
7001 								}
7002 								if (vel.y == 0)
7003 								{
7004 									//debugLog("Vel was 0");
7005 									vel = getWallNormal(TileVector(position));
7006 									vel.setLength2D(500);
7007 								}
7008 
7009 								if (vel.isLength2DIn(500))
7010 									vel.setLength2D(500);
7011 
7012 
7013 								vel.capLength2D(800);
7014 
7015 								position = lastPosition;
7016 								this->doCollisionAvoidance(1, 4, 0.5, 0, 500);
7017 
7018 
7019 								/*
7020 								vel = -vel;
7021 								if (vel.y < 0)
7022 									vel.y *= 2;
7023 								position = lastPosition;
7024 								*/
7025 							}
7026 							else
7027 							{
7028 								position = lastPosition;
7029 
7030 								// works as long as not buried in wall ... yep. =(
7031 								if (dsq->game->isObstructed(TileVector(position)))
7032 								{
7033 									//vel = -vel*0.5f;
7034 									vel = 0;
7035 								}
7036 								else
7037 								{
7038 									// && dsq->game->getPercObsInArea(position, 4) < 0.75f
7039 									if (!inCurrent)
7040 									{
7041 										if (bursting)
7042 										{
7043 											//vel = 0;
7044 										}
7045 										else
7046 										{
7047 											if (!_isUnderWater && dsq->continuity.form == FORM_FISH)
7048 											{
7049 												vel = 0;
7050 											}
7051 											else
7052 											{
7053 												// this bounce is what causes naija to get wedged
7054 												float len = vel.getLength2D();
7055 												Vector n = dsq->game->getWallNormal(position, 5);
7056 												if (n.x == 0 && n.y == 0)
7057 												{
7058 													vel = 0;
7059 												}
7060 												else
7061 												{
7062 													//n.setLength2D(len/2);
7063 													//vel += n;
7064 													n.setLength2D(len);
7065 													vel = (n+vel)*0.5f;
7066 													//vel = (n + vel)*0.5f;
7067 												}
7068 											}
7069 										}
7070 									}
7071 								}
7072 
7073 							}
7074 						}
7075 
7076 
7077 						if (!collided && checkWarpAreas()) collided = true;
7078 						if (collided) break;
7079 
7080 						// DO NOT COLLIDE WITH LR_ENTITIES
7081 						// ENTITY SCRIPTS WILL HANDLE Entity V. Entity COLLISIONS
7082 
7083 					}
7084 				}
7085 			}
7086 		}
7087 	}
7088 
7089 	//fuuugly
7090 	// you said it!
7091 	if (riding || boneLock.on)
7092 	{
7093 		checkWarpAreas();
7094 	}
7095 	applyRidingPosition();
7096 	updateHair(dt);
7097 	chargeEmitter->position = chargingEmitter->position = position + offset;
7098 
7099 	// particle sets
7100 	if (leftHandEmitter && rightHandEmitter && boneLeftHand && boneRightHand)
7101 	{
7102 		leftHandEmitter->position = boneLeftHand->getWorldCollidePosition(Vector(0, 16));
7103 		rightHandEmitter->position = boneRightHand->getWorldCollidePosition(Vector(0,16));
7104 	}
7105 
7106 	if(canCollideWithShots())
7107 		dsq->game->handleShotCollisions(this, (activeAura == AURA_SHIELD));
7108 
7109 
7110 	if(!core->particlesPaused && elementEffectMult > 0)
7111 	{
7112 		ElementUpdateList& elems = dsq->game->elementInteractionList;
7113 		for (ElementUpdateList::iterator it = elems.begin(); it != elems.end(); ++it)
7114 		{
7115 			(*it)->doInteraction(this, elementEffectMult, 16);
7116 		}
7117 	}
7118 }
7119 
7120 
checkNearWall()7121 void Avatar::checkNearWall()
7122 {
7123 	state.nearWall = false;
7124 
7125 	if (!inCurrent && bursting && !state.lockedToWall && !vel.isZero() && !riding && _isUnderWater)
7126 	{
7127 		int checkRange = 11;
7128 		Vector v = vel;
7129 		v.normalize2D();
7130 		TileVector oT(position);
7131 		TileVector t=oT,lastT=oT;
7132 		bool obs = false;
7133 		for (int i = 1; i < checkRange; i++)
7134 		{
7135 			t.x = oT.x + v.x*i;
7136 			t.y = oT.y + v.y*i;
7137 			if (dsq->game->isObstructed(t, ~OT_HURT))
7138 			{
7139 				obs = true;
7140 				break;
7141 			}
7142 			lastT = t;
7143 		}
7144 		if (obs)
7145 		{
7146 			Vector n = dsq->game->getWallNormal(t.worldVector());
7147 			if (!n.isZero())
7148 			{
7149 				state.nearWall = true;
7150 				float t=0.2;
7151 				rotateToVec(n, t, 0);
7152 				skeletalSprite.transitionAnimate("wall", t);
7153 			}
7154 			else
7155 				state.nearWall = false;
7156 		}
7157 		else
7158 		{
7159 			state.nearWall = false;
7160 		}
7161 	}
7162 }
7163 
onWarp()7164 void Avatar::onWarp()
7165 {
7166 	avatar->setv(EV_NOINPUTNOVEL, 0);
7167 	closeSingingInterface();
7168 }
7169 
checkWarpAreas()7170 bool Avatar::checkWarpAreas()
7171 {
7172 	int i = 0;
7173 	for (i = 0; i < dsq->game->getNumPaths(); i++)
7174 	{
7175 		bool warp = false;
7176 		Path *p = dsq->game->getPath(i);
7177 		if (p && p->active && !p->nodes.empty())
7178 		{
7179 			PathNode *n = &p->nodes[0];
7180 			if (n)
7181 			{
7182 				Vector backPos;
7183 				if (!p->vox.empty())
7184 				{
7185 					if (p->isCoordinateInside(position))
7186 					{
7187 						if (p->replayVox == 1)
7188 						{
7189 							dsq->voice(p->vox);
7190 							p->replayVox = 2;
7191 						}
7192 						else
7193 						{
7194 							dsq->voiceOnce(p->vox);
7195 						}
7196 					}
7197 				}
7198 				if (!p->warpMap.empty() && p->pathType == PATH_WARP)
7199 				{
7200 					int range = 512;
7201 					switch(p->warpType)
7202 					{
7203 					case 'C':
7204 						if ((position - n->position).getSquaredLength2D() < sqr(range))
7205 						{
7206 							warp = true;
7207 						}
7208 					break;
7209 					default:
7210 					{
7211 						if (p->isCoordinateInside(position))
7212 						{
7213 							warp = true;
7214 							backPos = p->getBackPos(position);
7215 						}
7216 					}
7217 					break;
7218 					}
7219 				}
7220 				if (warp)
7221 				{
7222 					if (avatar->canWarp)
7223 						dsq->game->warpToSceneFromNode(p);
7224 					else
7225 					{
7226 						avatar->position = backPos;
7227 						//avatar->vel = -avatar->vel * 0.5f;
7228 						Vector n = p->getEnterNormal();
7229 						n.setLength2D(avatar->vel.getLength2D());
7230 						avatar->vel += n;
7231 						avatar->vel *= 0.5f;
7232 					}
7233 					return true;
7234 				}
7235 				if (core->getNestedMains() == 1 && !riding)
7236 				{
7237 					if (p->warpMap.empty() && !p->warpNode.empty())
7238 					{
7239 						if (p->isCoordinateInside(position))
7240 						{
7241 							Path *p2 = dsq->game->getPathByName(p->warpNode);
7242 							if (p2)
7243 							{
7244 								dsq->game->preLocalWarp(p->localWarpType);
7245 								dsq->game->avatar->position = p2->getPathNode(0)->position;
7246 								dsq->game->postLocalWarp();
7247 								//dsq->fade(0, t);
7248 								return true;
7249 							}
7250 
7251 						}
7252 					}
7253 				}
7254 			}
7255 		}
7256 
7257 	}
7258 	for (i = 0; i < dsq->game->warpAreas.size(); i++)
7259 	{
7260 		WarpArea *a = &dsq->game->warpAreas[i];
7261 		if (a->radius)
7262 		{
7263 			Vector diff = a->position - this->position;
7264 			if (diff.getSquaredLength2D() < sqr(a->radius))
7265 			{
7266 				if (canWarp)
7267 				{
7268 					dsq->game->warpToArea(a);
7269 					return false;
7270 				}
7271 				else
7272 				{
7273 					position = lastPosition;
7274 					vel = -diff;
7275 					return true;
7276 				}
7277 			}
7278 		}
7279 		else
7280 		{
7281 			if (position.x > a->position.x - a->w && position.x < a->position.x + a->w)
7282 			{
7283 				if (position.y > a->position.y - a->h && position.y < a->position.y + a->h)
7284 				{
7285 					if (canWarp)
7286 					{
7287 						dsq->game->warpToArea(a);
7288 						return false;
7289 					}
7290 					else
7291 					{
7292 						position = lastPosition;
7293 						vel = -vel*1.1f;
7294 					}
7295 				}
7296 			}
7297 		}
7298 	}
7299 	return false;
7300 }
7301