Aquaria/Avatar.cpp
author Ryan C. Gordon <icculus@icculus.org>
Fri Jun 04 01:13:54 2010 -0400
changeset 1 95890e70f28e
child 29 7b265dc3088e
permissions -rw-r--r--
Use system-installed build of SDL on most platforms.

Removed the Linux SDL library from revision control and changed CMakeLists.txt
to require the system to have it preinstalled. The Mac (and eventually,
Windows) will use an included SDL build, but on Linux, getting this from the
package manager makes more sense.

Thanks to Pontos for the patch.
     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 int LAYER_FLOURISH = 3;
    48 
    49 const float MULT_DMG_CRABCOSTUME = 0.75;
    50 const float MULT_DMG_FISHFORM = 1.5;
    51 const float MULT_DMG_SEAHORSEARMOR = 0.6;
    52 
    53 const float MULT_MAXSPEED_BEASTFORM = 1.2;
    54 const float MULT_MAXSPEED_FISHFORM = 1.5;
    55 
    56 const float MULT_DMG_EASY	= 0.5;
    57 
    58 const float JELLYCOSTUME_HEALTHPERC		= 0.5;
    59 const float JELLYCOSTUME_HEALDELAY		= 2.0;
    60 const float	JELLYCOSTUME_HEALAMOUNT		= 0.5;
    61 
    62 const float biteTimerBiteRange = 0.6;
    63 const float biteTimerMax = 3;
    64 const float biteDelayPeriod = 0.08;
    65 const int normalTendrilHits = 3;
    66 const int rollTendrilHits = 4;
    67 const int maxTendrilHits = 6;
    68 
    69 const float fireDelayTime = 0.2;
    70 const int maxShieldPoints = 8;
    71 const int minMouse = 60;
    72 int SongIcon::notesOpen = 0;
    73 Avatar *avatar = 0;
    74 const Vector BLIND_COLOR = Vector(0.1, 0.1, 0.1);
    75 const float ANIM_TRANSITION	= 0.2;
    76 const float MANA_RECHARGE_RATE = 1.0;
    77 const int AURA_SHIELD_RADIUS = 64;
    78 //const int TARGET_RANGE = 1024;
    79 const int TARGET_RANGE = 1024; // 650
    80 const int TARGET_GRACE_RANGE = 200;
    81 //const int TARGET_RANGE = 700;
    82 //const int TARGET_RANGE = 64;
    83 const float NOTE_SCALE = 0.75;
    84 const int singingInterfaceRadius = 100;
    85 const int openSingingInterfaceRadius = 128;
    86 //164
    87 const int BURST_DISTANCE = 200;
    88 const int STOP_DISTANCE = 48;
    89 const int maxMouse = BURST_DISTANCE;
    90 //const int SHOCK_RANGE	= 700;
    91 const int SHOCK_RANGE	= 1000;
    92 const int SPIRIT_RANGE	= 2000;
    93 
    94 const float QUICK_SONG_CAST_DELAY = 0.4;
    95 
    96 const float BURST_RECOVER_RATE = 1.2; // 3.0 // 0.75
    97 const float BURST_USE_RATE = 1.5; //0.9 //1.5;
    98 const float BURST_DELAY = 0.1;
    99 const float BURST_ACCEL = 4000; //2000 // 1000
   100 
   101 const float TUMMY_TIME = 6.0;
   102 
   103 const float chargeMax = 2.0;
   104 
   105 volatile int micNote = -1;
   106 bool openedFromMicInput = false;
   107 
   108 const int requiredDualFormCharge = 3;
   109 
   110 bool usingDigital = false;
   111 
   112 Bone *bone_head = 0;
   113 Bone *bone_dualFormGlow = 0;
   114 
   115 
   116 bool _isUnderWater;
   117 
   118 //HRECORD avatarRecord = 0;
   119 
   120 #define ANIMLAYER_OVERRIDE			4
   121 #define ANIMLAYER_UPPERBODYIDLE		6
   122 #define ANIMLAYER_HEADOVERRIDE		7
   123 
   124 Vector Target::getWorldPosition()
   125 {
   126 	Vector ret;
   127 	if (e)
   128 	{
   129 		ret = e->getTargetPoint(targetPt);
   130 	}
   131 	return ret;
   132 }
   133 
   134 void Avatar::bindInput()
   135 {
   136 	ActionMapper::clearActions();
   137 	ActionMapper::clearCreatedEvents();
   138 
   139 
   140 	dsq->user.control.actionSet.importAction(this, "PrimaryAction", ACTION_PRIMARY);
   141 	dsq->user.control.actionSet.importAction(this, "SecondaryAction", ACTION_SECONDARY);
   142 
   143 	dsq->user.control.actionSet.importAction(this, "Revert", MakeFunctionEvent(Avatar, revert), 0);
   144 
   145 	dsq->user.control.actionSet.importAction(this, "SwimUp",		ACTION_SWIMUP);
   146 	dsq->user.control.actionSet.importAction(this, "SwimDown",		ACTION_SWIMDOWN);
   147 	dsq->user.control.actionSet.importAction(this, "SwimLeft",		ACTION_SWIMLEFT);
   148 	dsq->user.control.actionSet.importAction(this, "SwimRight",		ACTION_SWIMRIGHT);
   149 
   150 	/*
   151 	dsq->user.control.actionSet.importAction(this, "SingUp",		ACTION_SINGUP);
   152 	dsq->user.control.actionSet.importAction(this, "SingDown",		ACTION_SINGDOWN);
   153 	dsq->user.control.actionSet.importAction(this, "SingLeft",		ACTION_SINGLEFT);
   154 	dsq->user.control.actionSet.importAction(this, "SingRight",		ACTION_SINGRIGHT);
   155 	*/
   156 
   157 	dsq->user.control.actionSet.importAction(this, "SongSlot1",		ACTION_SONGSLOT1);
   158 	dsq->user.control.actionSet.importAction(this, "SongSlot2",		ACTION_SONGSLOT2);
   159 	dsq->user.control.actionSet.importAction(this, "SongSlot3",		ACTION_SONGSLOT3);
   160 	dsq->user.control.actionSet.importAction(this, "SongSlot4",		ACTION_SONGSLOT4);
   161 	dsq->user.control.actionSet.importAction(this, "SongSlot5",		ACTION_SONGSLOT5);
   162 	dsq->user.control.actionSet.importAction(this, "SongSlot6",		ACTION_SONGSLOT6);
   163 	dsq->user.control.actionSet.importAction(this, "SongSlot7",		ACTION_SONGSLOT7);
   164 	dsq->user.control.actionSet.importAction(this, "SongSlot8",		ACTION_SONGSLOT8);
   165 	dsq->user.control.actionSet.importAction(this, "SongSlot9",		ACTION_SONGSLOT9);
   166 	dsq->user.control.actionSet.importAction(this, "SongSlot10",	ACTION_SONGSLOT10);
   167 	
   168 	dsq->user.control.actionSet.importAction(this, "Look",			ACTION_LOOK);
   169 
   170 	/*
   171 	dsq->user.control.actionSet.importAction(this, "SongSlot5", "f5");
   172 	dsq->user.control.actionSet.importAction(this, "SongSlot6", "f6");
   173 	dsq->user.control.actionSet.importAction(this, "SongSlot7", "f7");
   174 	dsq->user.control.actionSet.importAction(this, "SongSlot8", "f8");
   175 	*/
   176 	
   177 	dsq->user.control.actionSet.importAction(this, "Roll",			ACTION_ROLL);
   178 
   179 	/*
   180 	// song note keys
   181 	addAction("s1", KEY_1);
   182 	addAction("s2", KEY_2);
   183 	addAction("s3", KEY_3);
   184 	addAction("s4", KEY_4);
   185 	addAction("s5", KEY_5);
   186 	addAction("s6", KEY_6);
   187 	addAction("s7", KEY_7);
   188 	addAction("s8", KEY_8);
   189 	*/
   190 
   191 }
   192 
   193 int Avatar::getNotesOpen()
   194 {
   195 	return SongIcon::notesOpen;
   196 }
   197 
   198 // note: z is set to 1.0 when we want the aim to be used as the shot direction
   199 // otherwise the shot will head straight to the target
   200 Vector Avatar::getAim()
   201 {
   202 	Vector d;
   203 	if (dsq->inputMode == INPUT_JOYSTICK)
   204 	{
   205 		if (!core->joystick.rightStick.isZero())
   206 		{
   207 			d = core->joystick.rightStick * 300;
   208 			d.z = 1;
   209 		}
   210 		else
   211 		{
   212 			d = core->joystick.position * 300;
   213 			d.z = 0;
   214 		}
   215 	}
   216 	else if (dsq->inputMode == INPUT_KEYBOARD)
   217 	{
   218 		d = dsq->getGameCursorPosition() - position;
   219 		d.z = 1;
   220 	}
   221 	else
   222 	{
   223 		d = dsq->getGameCursorPosition() - position;
   224 		d.z = 1;
   225 	}
   226 	if (d.isZero())
   227 		d = getNormal();
   228 	return d;
   229 }
   230 
   231 void Avatar::postInit()
   232 {
   233 	// post init isn't early enough
   234 	/*
   235 	Entity::postInit();
   236 	*/
   237 }
   238 
   239 void Avatar::onAnimationKeyPassed(int key)
   240 {
   241 	if (swimming && !isRolling() && !bursting && _isUnderWater)
   242 	{
   243 		if (key == 0 || key == 2)
   244 		{
   245 			//core->sound->playSfx("SwimKick", 255, 0, 1000+getMaxSpeed()/10.0);
   246 		}
   247 	}
   248 	Entity::onAnimationKeyPassed(key);
   249 }
   250 
   251 void Avatar::doBounce()
   252 {
   253 	float ba = 0.75;
   254 	if (isRolling())
   255 		ba = 1.0;
   256 	float len = vel.getLength2D();
   257 	Vector I = vel/len;
   258 	Vector N = dsq->game->getWallNormal(position);
   259 
   260 	if (!N.isZero())
   261 	{
   262 		//2*(-I dot N)*N + I
   263 		vel = 2*(-I.dot(N))*N + I;
   264 		vel.setLength2D(len*ba);
   265 	}
   266 }
   267 
   268 Vector randCirclePos(Vector position, int radius)
   269 {
   270 	float a = ((rand()%360)*(2*PI))/360.0;
   271 	return position + Vector(sinf(a), cosf(a))*radius;
   272 }
   273 
   274 SongIconParticle::SongIconParticle(Vector color, Vector pos, int note)
   275 								:  note(note)
   276 {
   277 	cull = false;
   278 	//fastTransform = true;
   279 	setTexture("particles/glow");
   280 
   281 	setWidthHeight(32);
   282 
   283 	float life = 1.0;
   284 
   285 	toIcon = 0;
   286 	this->color = color;
   287 	position = pos;
   288 
   289 	alpha.path.addPathNode(0, 0);
   290 	alpha.path.addPathNode(0.4, 0.2); // .8
   291 	alpha.path.addPathNode(0.2, 0.8); // .4
   292 	alpha.path.addPathNode(0, 1);
   293 	alpha.startPath(life);
   294 
   295 	scale.path.addPathNode(Vector(0.5,0.5), 0);
   296 	scale.path.addPathNode(Vector(1,1), 0.5);
   297 	scale.path.addPathNode(Vector(0.5,0.5), 1);
   298 	scale.startPath(life);
   299 
   300 	setLife(life);
   301 	setDecayRate(1);
   302 
   303 	//if (rand()%6 <= 2)
   304 	setBlendType(RenderObject::BLEND_ADD);
   305 
   306 	float smallestDist = -1;
   307 	SongIcon *closest = 0;
   308 	for (int i = 0; i < avatar->songIcons.size(); i++)
   309 	{
   310 		if (i != note)
   311 		{
   312 			Vector diff = (position - avatar->songIcons[i]->position);
   313 			float dist = diff.getSquaredLength2D();
   314 			if (smallestDist == -1 || dist < smallestDist)
   315 			{
   316 				smallestDist = dist;
   317 				closest = avatar->songIcons[i];
   318 			}
   319 		}
   320 	}
   321 	// find nearest song icon
   322 	if (closest)
   323 	{
   324 		toIcon = closest;
   325 	}
   326 }
   327 
   328 void SongIconParticle::onUpdate(float dt)
   329 {
   330 	Quad::onUpdate(dt);
   331 
   332 	if (toIcon)
   333 	{
   334 		Vector add = (toIcon->position - position);
   335 		add.setLength2D(200*dt);
   336 		velocity += add;
   337 		velocity.capLength2D(50);
   338 	}
   339 }
   340 
   341 SongIcon::SongIcon(int note) : Quad(), note(note)
   342 {
   343 	open = false;
   344 	alphaMod = 0.9;
   345 	/*
   346 	std::ostringstream os;
   347 	os << "SongIcon" << note;
   348 	setTexture(os.str());
   349 	*/
   350 	//setTexture("Cursor-Sing");
   351 	std::ostringstream os;
   352 	os << "Song/NoteSymbol" << note;
   353 	os.str();
   354 	setTexture(os.str());
   355 
   356 	scale = Vector(NOTE_SCALE, NOTE_SCALE);
   357 	cursorIsIn = false;
   358 	delay = 0;
   359 	counter = 0;
   360 	width = 40;
   361 	height = 40;
   362 
   363 	minTime = 0;
   364 	ptimer = 0;
   365 	noteColor = dsq->getNoteColor(note);
   366 	//color = dsq->getNoteColor(note)*0.75 + Vector(1,1,1)*0.25;
   367 	color = dsq->getNoteColor(note);
   368 	len = 0;
   369 
   370 	channel = BBGE_AUDIO_NOCHANNEL;
   371 
   372 	rippleTimer = 0;
   373 
   374 	glow = new Quad;
   375 	glow->setTexture("particles/bigglow");
   376 	glow->followCamera = 1;
   377 	glow->rotation.interpolateTo(Vector(0,0,360), 10, -1);
   378 	glow->alpha = 0;
   379 	glow->setBlendType(RenderObject::BLEND_ADD);
   380 	glow->scale = Vector(0.5, 0.5);
   381 	glow->color = dsq->getNoteColor(note);
   382 	dsq->game->addRenderObject(glow, LR_PARTICLES2);
   383 }
   384 
   385 void SongIcon::destroy()
   386 {
   387 	Quad::destroy();
   388 }
   389 
   390 void SongIcon::spawnParticles(float dt)
   391 {
   392 	float intv = 0.1;
   393 	// do stuff!
   394 	ptimer += dt;
   395 	while (ptimer > intv)
   396 	{
   397 		ptimer -= intv;
   398 		SongIconParticle *s = new SongIconParticle(noteColor, randCirclePos(position, 16), note);
   399 		s->followCamera = true;
   400 		dsq->game->addRenderObject(s, LR_HUD);
   401 	}
   402 }
   403 
   404 void SongIcon::onUpdate(float dt)
   405 {
   406 	Quad::onUpdate(dt);
   407 
   408 	if (!avatar->singing)
   409 		return;
   410 
   411 	if (alpha.x == 0 && !alpha.isInterpolating())
   412 		alpha.interpolateTo(0.3, 0.1);
   413 	if (delay > 0)
   414 	{
   415 		delay -= dt;
   416 		if (delay < 0)
   417 		{
   418 			delay = 0;
   419 			//channel = BBGE_AUDIO_NOCHANNEL;
   420 		}
   421 	}
   422 	if (counter > 0)
   423 	{
   424 		counter -= dt;
   425 		if (counter < 0)
   426 		{
   427 			counter = 0;
   428 			closeNote();
   429 		}
   430 	}
   431 	if (alpha.x > 0.5)
   432 	{
   433 		spawnParticles(dt);
   434 	}
   435 	if (open)
   436 	{
   437 		len += dt;
   438 		avatar->setHeadTexture("Singing", 0.1);
   439 	}
   440 	if (alpha.x == 1)
   441 	{
   442 		if ((!openedFromMicInput && isCoordinateInRadius(core->mouse.position, 25)) || micNote == note) // 40 width.x
   443 		{
   444 			//if (delay == 0)
   445 			if (true)
   446 			{
   447 				if (!cursorIsIn)
   448 				// highlighted for the first time
   449 				{
   450 					cursorIsIn = true;
   451 					openNote();
   452 				}
   453 				else
   454 				{
   455 					if (minTime > 0)
   456 					{
   457 						minTime -= dt;
   458 						if (minTime < 0)
   459 						{
   460 							minTime = 0;
   461 						}
   462 					}
   463 
   464 				}
   465 			}
   466 		}
   467 		else
   468 		{
   469 			if (cursorIsIn)
   470 			{
   471 				cursorIsIn = false;
   472 				closeNote();
   473 			}
   474 		}
   475 	}
   476 	if (alpha.x <= 0 && delay == 0) //  && channel != BBGE_AUDIO_NOCHANNEL
   477 	{
   478 		closeNote();
   479 	}
   480 
   481 	if (open)
   482 	{
   483 		if (dsq->user.video.noteEffects)
   484 		{
   485 			rippleTimer -= dt;
   486 			if (rippleTimer <= 0)
   487 			{
   488 				//rippleTimer = 1.0 - ((7 - note)/7.0)*0.7;
   489 				rippleTimer = 0.5 - (note/7.0)*0.4;
   490 
   491 				if (core->afterEffectManager)
   492 					core->afterEffectManager->addEffect(new ShockEffect(position,core->screenCenter,0.02,0.015,22,0.2f, 1.2));
   493 			}
   494 		}
   495 	}
   496 
   497 	if (glow)
   498 	{
   499 		glow->position = position;
   500 	}
   501 }
   502 
   503 void SongIcon::openNote()
   504 {
   505 	//if (delay > 0) return;
   506 	scale.interpolateTo(Vector(1.2, 1.2), 0.1);
   507 
   508 	if (dsq->user.video.noteEffects)
   509 	{
   510 		glow->scale = Vector(0.5,0.5);
   511 		glow->scale.interpolateTo(Vector(1.0, 1.0), 2, -1, 1, 1);
   512 
   513 		glow->alpha.interpolateTo(0.6, 0.2, 0, 0, 1);
   514 	}
   515 
   516 	/*
   517 	std::ostringstream os;
   518 	os << "Note"
   519 	*/
   520 
   521 	std::string sfx = dsq->game->getNoteName(note);
   522 
   523 	open = true;
   524 
   525 	internalOffset = Vector(-5, 0);
   526 	internalOffset.interpolateTo(Vector(5, 0), 0.08, -1, 1);
   527 
   528 	avatar->singNote(this->note);
   529 
   530 	// this should never get called:
   531 	if (channel != BBGE_AUDIO_NOCHANNEL)
   532 	{
   533 		dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
   534 		//dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
   535 		channel = BBGE_AUDIO_NOCHANNEL;
   536 	}
   537 	//dsq->sound->stopSfx(channel);
   538 
   539 
   540 	PlaySfx play;
   541 	play.name = sfx;
   542 	play.channel = 1 + note;
   543 	channel = dsq->sound->playSfx(play);
   544 
   545 
   546 	rippleTimer = 0;
   547 
   548 	minTime = 0.05;
   549 	counter = 3.2;
   550 
   551 	float glowLife = 0.5;
   552 
   553 	Quad *q = new Quad("particles/glow", position);
   554 	q->scale.interpolateTo(Vector(10, 10), glowLife+0.1);
   555 	q->alpha.path.addPathNode(0,0);
   556 	q->alpha.path.addPathNode(0.75,0.2);
   557 	q->alpha.path.addPathNode(0,1);
   558 	q->alpha.startPath(glowLife);
   559 	q->color = dsq->getNoteColor(note); //*0.5 + Vector(0.5, 0.5, 0.5)
   560 	q->setBlendType(RenderObject::BLEND_ADD);
   561 	q->followCamera = 1;
   562 	dsq->game->addRenderObject(q, LR_HUD);
   563 
   564 	{
   565 	std::ostringstream os2;
   566 	os2 << "Song/NoteSymbol" << note;
   567 
   568 	Quad *q = new Quad(os2.str(), position);
   569 	q->color = 0;
   570 	q->scale = Vector(0.5,0.5);
   571 	q->scale.interpolateTo(Vector(2, 2), glowLife+0.1);
   572 	//q->scale.interpolateTo(Vector(10, 10), glowLife+0.1);
   573 	q->alpha.path.addPathNode(0,0);
   574 	q->alpha.path.addPathNode(0.5,0.2);
   575 	q->alpha.path.addPathNode(0,1);
   576 	q->alpha.startPath(glowLife);
   577 	//q->setBlendType(RenderObject::BLEND_ADD);
   578 	q->followCamera = 1;
   579 	dsq->game->addRenderObject(q, LR_HUD);
   580 	}
   581 
   582 	avatar->songInterfaceTimer = 1.0;
   583 
   584 	notesOpen++;
   585 	/*
   586 	std::ostringstream os2;
   587 	os2 << "notesOpen: " << notesOpen;
   588 	debugLog(os2.str());
   589 	*/
   590 	if (notesOpen > 0)
   591 	{
   592 		len = 0;
   593 
   594 		FOR_ENTITIES(i)
   595 		{
   596 			Entity *e = *i;
   597 			if ((e->position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
   598 			{
   599 				e->songNote(note);
   600 			}
   601 		}
   602 		for (int i = 0; i < dsq->game->paths.size(); i++)
   603 		{
   604 			Path *p = dsq->game->paths[i];
   605 			if (!p->nodes.empty())
   606 			{
   607 				if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
   608 				{
   609 					p->songNote(note);
   610 				}
   611 			}
   612 		}
   613 	}
   614 }
   615 
   616 void SongIcon::closeNote()
   617 {
   618 	//if (delay > 0) return;
   619 	scale.interpolateTo(Vector(NOTE_SCALE, NOTE_SCALE), 0.1);
   620 
   621 	if (dsq->game->avatar->isSinging() && dsq->user.video.noteEffects)
   622 		glow->alpha.interpolateTo(0.3, 1.5, 0, 0, 1);
   623 	else
   624 		glow->alpha.interpolateTo(0, 1.5, 0, 0, 1);
   625 	glow->scale.interpolateTo(Vector(0.5, 0.5), 0.5);
   626 
   627 
   628 	cursorIsIn = false;
   629 
   630 	if (channel != BBGE_AUDIO_NOCHANNEL)
   631 	{
   632 		dsq->sound->fadeSfx(channel, SFT_OUT, 1.0);
   633 		channel = BBGE_AUDIO_NOCHANNEL;
   634 		//delay = 0.5;
   635 	}
   636 
   637 	if (open)
   638 	{
   639 		internalOffset.stop();
   640 		internalOffset = Vector(0,0);
   641 		notesOpen--;
   642 		open = false;
   643 
   644 		FOR_ENTITIES(i)
   645 		{
   646 			Entity *e = *i;
   647 			int dist = (e->position - dsq->game->avatar->position).getSquaredLength2D();
   648 			if (e != dsq->game->avatar && dist < sqr(1000))
   649 			{
   650 				e->songNoteDone(note, len);
   651 			}
   652 		}
   653 		for (int i = 0; i < dsq->game->paths.size(); i++)
   654 		{
   655 			Path *p = dsq->game->paths[i];
   656 			if (!p->nodes.empty())
   657 			{
   658 				if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
   659 				{
   660 					p->songNoteDone(note, len);
   661 				}
   662 			}
   663 		}
   664 	}
   665 
   666 	/*
   667 	std::ostringstream os;
   668 	os << "notesOpen: " << notesOpen;
   669 	debugLog(os.str());
   670 	*/
   671 
   672 	if (notesOpen <= 0)
   673 	{
   674 		notesOpen = 0;
   675 		if (dsq->continuity.form == FORM_NORMAL)
   676 			avatar->setHeadTexture("");
   677 	}
   678 }
   679 
   680 void SongIcon::openInterface()
   681 {
   682 	delay = 0;
   683 	alpha.interpolateTo(1, 0.1);
   684 }
   685 
   686 void SongIcon::closeInterface()
   687 {
   688 	closeNote();
   689 	delay = 0;
   690 	alpha.interpolateTo(0, 0.1);
   691 }
   692 
   693 AvatarState::AvatarState()
   694 {
   695 	abilityDelay = 0;
   696 	outOfWaterTimer = 0;
   697 	backFlip = false;
   698 	nearWall = false;
   699 	wasUnderWater = true;
   700 	blind = false;
   701 	lockedToWall = false;
   702 	crawlingOnWall = false;
   703 	shotDelay = 0;
   704 	spellCharge = 0;
   705 	leachTimer = 0;
   706 	swimTimer = 0;
   707 	rollTimer = 0;
   708 	updateLookAtTime = 0;
   709 	lookAtEntity = 0;
   710 	blinkTimer = 0;
   711 }
   712 
   713 //    0  1  2  3  4  5  6  7  8  9
   714 const int	spellManaCost	[]	= { 0,    3,    1,    1,   2,    1,    4,    1,    1,    2};
   715 const float spellChargeMins	[]	= { 0,    1.25, 0,    0,   0.75, 0,    0.5,  0,    0,    3};
   716 //const float spellChargeMaxs	[]	= { 0.75, 1.5,  1.9,  1,   1,    1,    0.75, 1,    1,    3};
   717 const float spellCastDelays	[]	= { 0.05, 0.2,  0.4, 0.1, 0.1,  0.1,  0.2,  0.1,  0.1,  0.1 };
   718 
   719 bool avatarDebugEnabled = false;
   720 
   721 void Avatar::toggleMovement(bool on)
   722 {
   723 	canMove = on;
   724 }
   725 
   726 bool Avatar::isLockable()
   727 {
   728 	return (bursting || !_isUnderWater) && (boneLockDelay == 0) && (dsq->continuity.form != FORM_FISH);
   729 }
   730 
   731 bool Avatar::isSinging()
   732 {
   733 	return singing;
   734 }
   735 
   736 void Avatar::shift()
   737 {
   738 	dsq->continuity.shiftWorlds();
   739 }
   740 
   741 void Avatar::applyWorldEffects(WorldType type)
   742 {
   743 	static bool oldfh=false;
   744 
   745 	if (type == WT_SPIRIT)
   746 	{
   747 		//skeletalSprite.transitionAnimate("ball", 0.1, -1);
   748 		//skeletalSprite.alpha.interpolateTo(0, 1);
   749 		//skeletalSprite.alpha = 0;
   750 		//dsq->game->addRenderObject(&skeletalSprite, LR_ENTITIES);
   751 
   752 		removeChild(&skeletalSprite);
   753 		skeletalSprite.position = position;
   754 		skeletalSprite.setFreeze(true);
   755 		skeletalSprite.scale = scale;
   756 		skeletalSprite.alpha.interpolateTo(0.5, 1);
   757 		//dsq->game->addRenderObject(&skeletalSprite, LR_ENTITIES);
   758 		skeletalSprite.rotation.z = rotation.z;
   759 		skeletalSprite.rotationOffset.z = rotationOffset.z;
   760 
   761 
   762 		oldfh = skeletalSprite.isfh();
   763 		skeletalSprite.fhTo(isfh());
   764 
   765 		renderQuad = true;
   766 		setTexture("glow");
   767 		width = 256;
   768 		height = 256;
   769 		setBlendType(BLEND_ADD);
   770 		fader->alpha.interpolateTo(0.75, 1);
   771 
   772 		dsq->sound->toggleEffectMusic(SFX_FLANGE, true);
   773 	}
   774 	else
   775 	{
   776 		//skeletalSprite.transitionAnimate("idle", 1, -1);
   777 		//skeletalSprite.alpha.interpolateTo(1, 1);
   778 		//skeletalSprite.alpha = 1;
   779 		//dsq->game->removeRenderObject(&skeletalSprite);
   780 
   781 		skeletalSprite.setFreeze(false);
   782 		if (!skeletalSprite.getParent())
   783 		{
   784 			addChild(&skeletalSprite, PM_STATIC);
   785 		}
   786 		skeletalSprite.position = Vector(0,0,0);
   787 		skeletalSprite.scale = Vector(1,1,1);
   788 		renderQuad = false;
   789 		setBlendType(BLEND_DEFAULT);
   790 		fader->alpha.interpolateTo(0, 1);
   791 
   792 		bool newfh = skeletalSprite.isfh();
   793 		skeletalSprite.fhTo(oldfh);
   794 		skeletalSprite.rotation.z = 0;
   795 		skeletalSprite.rotationOffset.z = 0;
   796 
   797 		fhTo(newfh);
   798 
   799 		dsq->sound->toggleEffectMusic(SFX_FLANGE, false);
   800 	}
   801 }
   802 
   803 void Avatar::startFlourish()
   804 {
   805 	std::string anim = dsq->continuity.getInternalFormName() + "-flourish";
   806 	//if (skeletalSprite.getAnimation(anim))
   807 	Animation *fanim = skeletalSprite.getAnimation(anim);
   808 	if (fanim)
   809 	{
   810 		flourishTimer.start(fanim->getAnimationLength()-0.2);
   811 		flourishPowerTimer.start(fanim->getAnimationLength()*0.5);
   812 	}
   813 	skeletalSprite.transitionAnimate(anim, 0.1, 0, LAYER_FLOURISH);
   814 	flourish = true;
   815 
   816 	float rotz = rotationOffset.z;
   817 	if (this->isfh())
   818 		rotationOffset = Vector(0,0,rotz+360);
   819 	else
   820 		rotationOffset = Vector(0,0,rotz-360);
   821 
   822 	FormType f = dsq->continuity.form;
   823 	if (f != FORM_NORMAL && f != FORM_BEAST && f != FORM_FISH && f != FORM_SUN && f != FORM_NATURE)
   824 	{
   825 		rotationOffset.z *= -1;
   826 	}
   827 	if (f == FORM_ENERGY || f == FORM_DUAL)
   828 	{
   829 		rotationOffset.z *= 2;
   830 	}
   831 
   832 	if (f == FORM_BEAST)
   833 	{
   834 		Vector v = getNormal();
   835 		if (!v.isZero())
   836 		{
   837 			v *= 400;
   838 			vel += v;
   839 		}
   840 	}
   841 
   842 	rotationOffset.interpolateTo(Vector(0,0,rotz), 0.8, 0, 0, 1);
   843 }
   844 
   845 void Avatar::onIdle()
   846 {
   847 	if (dsq->game->li)
   848 	{
   849 		if (dsq->game->li->getState() == STATE_HUG && riding)
   850 		{
   851 			dsq->game->li->setState(STATE_IDLE);
   852 		}
   853 	}
   854 	//stillTimer.stop();
   855 	stopBurst();
   856 	if (movingOn)
   857 	{
   858 		dsq->setMousePosition(Vector(400,300));
   859 	}
   860 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
   861 	stopRoll();
   862 	closeSingingInterface();
   863 	fallOffWall();
   864 
   865 	dsq->gameSpeed.stopPath();
   866 	dsq->gameSpeed.interpolateTo(1,0);
   867 }
   868 
   869 std::string Avatar::getBurstAnimName()
   870 {
   871 	std::string ret;
   872 	switch(dsq->continuity.form)
   873 	{
   874 	case FORM_ENERGY:
   875 		ret = "energyburst";
   876 	break;
   877 	default:
   878 		ret = "burst";
   879 	break;
   880 	}
   881 	return ret;
   882 }
   883 
   884 std::string Avatar::getRollAnimName()
   885 {
   886 	std::string ret;
   887 	switch(dsq->continuity.form)
   888 	{
   889 	case FORM_ENERGY:
   890 		ret = "energyroll";
   891 	break;
   892 	default:
   893 		ret = "roll";
   894 	break;
   895 	}
   896 	return ret;
   897 }
   898 
   899 std::string Avatar::getIdleAnimName()
   900 {
   901 	std::string ret="idle";
   902 	switch(dsq->continuity.form)
   903 	{
   904 	case FORM_ENERGY:
   905 		ret="energyidle";
   906 	break;
   907 	}
   908 	return ret;
   909 }
   910 
   911 void Avatar::clampPosition()
   912 {
   913 	lastPosition = position;
   914 }
   915 
   916 void Avatar::updatePosition()
   917 {
   918 	updateHair(0);
   919 }
   920 
   921 void Avatar::debugMsg(const std::string &msg)
   922 {
   923 	if (avatarDebugEnabled)
   924 	{
   925 		BitmapText *txt = new BitmapText(&dsq->font);
   926 		txt->setLife(2);
   927 		txt->setDecayRate(1);
   928 		txt->position = this->position;
   929 		txt->setText(msg);
   930 		txt->fadeAlphaWithLife = true;
   931 		core->getTopStateData()->addRenderObject(txt, LR_DEBUG_TEXT);
   932 	}
   933 }
   934 
   935 void Avatar::onBlindTest()
   936 {
   937 	setBlind(5);
   938 }
   939 
   940 void Avatar::onToggleDebugMessages()
   941 {
   942 	avatarDebugEnabled = !avatarDebugEnabled;
   943 }
   944 
   945 void Avatar::updateHair(float dt)
   946 {
   947 	static float hairTimer = 0;
   948 	Bone *b = skeletalSprite.getBoneByIdx(0);
   949 	if (hair && b)
   950 	{
   951 		hair->alpha.x = alpha.x;
   952 		hair->color.x = color.x * multColor.x * dsq->game->sceneColor.x * dsq->game->sceneColor2.x * dsq->game->sceneColor3.x;
   953 		hair->color.y = color.y * multColor.y * dsq->game->sceneColor.y * dsq->game->sceneColor2.y * dsq->game->sceneColor3.y;
   954 		hair->color.z = color.z * multColor.z * dsq->game->sceneColor.z * dsq->game->sceneColor2.z * dsq->game->sceneColor3.z;
   955 		Vector headPos = b->getWorldCollidePosition(Vector(12,-32,0));
   956 
   957 		hair->setHeadPosition(headPos);
   958 		Vector diff = headPos - position;
   959 
   960 		Vector diff2;
   961 		if (!isfh())
   962 			diff2 = diff.getPerpendicularLeft();
   963 		else
   964 			diff2 = diff.getPerpendicularRight();
   965 
   966 		Vector diff3 = position - headPos;
   967 
   968 		if (state.lockedToWall && wallPushVec.y < 0 && (fabs(wallPushVec.y) > fabs(wallPushVec.x)))
   969 		{
   970 			if (isfh())
   971 			{
   972 				diff3 = Vector(-50, -25);
   973 			}
   974 			else
   975 				diff3 = Vector(50,-25);
   976 		}
   977 
   978 		float len =diff2.getLength2D();
   979 		diff3.setLength2D(len);
   980 		/*
   981 		diff.y = -diff.y;
   982 		diff = (diff + diff2)/2.0;
   983 		*/
   984 
   985 		hairTimer += dt;
   986 		while (hairTimer > 2.0)
   987 		{
   988 			hairTimer -= 2.0;
   989 		}
   990 		float useTimer = hairTimer;
   991 		if (useTimer > 1.0)
   992 			useTimer = 1.0 - (hairTimer-1);
   993 		float frc = 0.333333;
   994 		diff = (diff2*(frc*(1.0-(useTimer*0.5))) + diff3*(frc) + Vector(0,len)*(frc*(0.5+useTimer*0.5)));
   995 
   996 
   997 
   998 		if (_isUnderWater)
   999 		{
  1000 			diff.setLength2D(400);
  1001 			//if (!vel.isLength2DIn(10))
  1002 			hair->exertForce(diff, dt);
  1003 		}
  1004 		else
  1005 		{
  1006 			diff.setLength2D(400);
  1007 			hair->exertForce(diff, dt);
  1008 		}
  1009 		if (!vel2.isZero())
  1010 			hair->exertForce(vel2, dt);
  1011 		hair->updatePositions();
  1012 	}
  1013 }
  1014 
  1015 void Avatar::updateDamageVisualEffects()
  1016 {
  1017    	int damageThreshold = float(maxHealth/5.0)*3.0f;
  1018 	if (health <= damageThreshold)
  1019 	{
  1020 		//dsq->game->damageSprite->alpha.interpolateTo(0.9, 0.5);
  1021 		float a = ((damageThreshold - health)/float(damageThreshold))*1.0;
  1022 		dsq->game->damageSprite->alpha.interpolateTo(a, 0.3);
  1023 
  1024 		/*
  1025 		std::ostringstream os;
  1026 		os << "damageSprite alpha: " << a;
  1027 		debugLog(os.str());
  1028 		*/
  1029 
  1030 		dsq->game->damageSprite->scale = Vector(1,1);
  1031 		dsq->game->damageSprite->scale.interpolateTo(Vector(1.2, 1.2), 0.5, -1, 1);
  1032 
  1033 		/*
  1034 		if (health <= 0)
  1035 		{
  1036 			dsq->game->sceneColor.interpolateTo(Vector(1,0.5,0.5), 0.75);
  1037 		}
  1038 		*/
  1039 	}
  1040 	else
  1041 	{
  1042 		dsq->game->damageSprite->alpha.interpolateTo(0, 0.3);
  1043 	}
  1044 }
  1045 
  1046 void Avatar::checkUpgradeForShot(Shot *s)
  1047 {
  1048 	if (dsq->continuity.energyMult <= 1)
  1049 		s->extraDamage = dsq->continuity.energyMult * 1;
  1050 	else
  1051 		s->extraDamage = dsq->continuity.energyMult * 0.75;
  1052 
  1053 	if (s->extraDamage > 0)
  1054 	{
  1055 		Quad *glow = new Quad("particles/glow", Vector(0,0));
  1056 		glow->color = Vector(1,0,0);
  1057 		glow->color.interpolateTo(Vector(1,0.5,0.5), 0.1, -1, 1);
  1058 		glow->setBlendType(BLEND_ADD);
  1059 		glow->scale = Vector(4, 4) + (s->extraDamage*Vector(2,2));
  1060 		glow->scale.interpolateTo(Vector(16,16)+ (s->extraDamage*Vector(2,2)), 0.5, -1, 1);
  1061 		s->addChild(glow, PM_POINTER);
  1062 	}
  1063 }
  1064 
  1065 void Avatar::onDamage(DamageData &d)
  1066 {
  1067 	Entity::onDamage(d);
  1068 
  1069 
  1070 	if (dsq->difficulty == DSQ::DIFF_EASY)
  1071 	{
  1072 		if (d.damage > 0)
  1073 			d.damage *= MULT_DMG_EASY;
  1074 	}
  1075 
  1076 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
  1077 	if (dsq->continuity.form == FORM_NORMAL)
  1078 	{
  1079 		if (nocasecmp(dsq->continuity.costume, "CC")==0)
  1080 		{
  1081 			d.damage *= MULT_DMG_CRABCOSTUME;
  1082 		}
  1083 	}
  1084 
  1085 	if (riding != 0)
  1086 	{
  1087 		if (nocasecmp(dsq->continuity.costume, "seahorse")==0)
  1088 		{
  1089 			d.damage *= MULT_DMG_SEAHORSEARMOR;
  1090 		}
  1091 	}
  1092 
  1093 	if (dsq->continuity.form == FORM_FISH)
  1094 	{
  1095 		d.damage *= MULT_DMG_FISHFORM;
  1096 	}
  1097 
  1098 	if ((core->isNested() && dsq->game->invincibleOnNested) || dsq->game->invinciblity)
  1099 	{
  1100 		d.damage = 0;
  1101 		d.damageType = DT_NONE;
  1102 		return;
  1103 	}
  1104 
  1105 	if (d.damageType == DT_ENEMY_INK)
  1106 	{
  1107 		setBlind(d.effectTime);
  1108 		return;
  1109 	}
  1110 
  1111 	if (d.damageType == DT_ENEMY_POISON)
  1112 	{
  1113 		dsq->continuity.setPoison(1, d.effectTime);
  1114 	}
  1115 
  1116 	if (dsq->continuity.defenseMultTimer.isActive())
  1117 	{
  1118 		d.damage *= dsq->continuity.defenseMult;
  1119 	}
  1120 
  1121 	if (dsq->continuity.invincibleTimer.isActive())
  1122 		d.damage = 0;
  1123 
  1124 	if (!canDie)
  1125 	{
  1126 		if ((health - d.damage) <= 0)
  1127 		{
  1128 			float i = d.damage;
  1129 			while (i >= 0)
  1130 			{
  1131 				if ((health - i) > 0)
  1132 				{
  1133 					d.damage = i;
  1134 					break;
  1135 				}
  1136 
  1137 				i -= 0.5;
  1138 			}
  1139 		}
  1140 	}
  1141 
  1142 	if ((!invincible || !dsq->game->invincibleOnNested) && !(invincibleBreak && damageTimer.isActive() && d.useTimer) && !dsq->continuity.invincibleTimer.isActive())
  1143 	{
  1144 		if (d.damageType == DT_ENEMY_ACTIVEPOISON)
  1145 			core->sound->playSfx("Poison");
  1146 		else
  1147 			core->sound->playSfx("Pain");
  1148 			
  1149 
  1150 		setHeadTexture("Pain", 1);
  1151 
  1152 		int r = (rand()%2)+1;
  1153 		std::ostringstream os;
  1154 		os << "basicHit" << r;
  1155 		skeletalSprite.transitionAnimate(os.str(), 0.05, 0, ANIMLAYER_OVERRIDE);
  1156 
  1157 		/*
  1158 		if (d.attacker)
  1159 		{
  1160 			// this will probably cause a crash!
  1161 			state.lookAtEntity = d.attacker;
  1162 		}
  1163 		*/
  1164 
  1165 		if (d.damage > 0)
  1166 		{
  1167 			float healthWillBe = health-d.damage;
  1168 			// determines length of shader blur as well
  1169 			float t = 0.5;
  1170 			if (healthWillBe<=0)
  1171 				t = 2;
  1172 
  1173 			dsq->rumble(d.damage, d.damage, 0.4);
  1174 			if (d.damage > 0)
  1175 			{
  1176 				//dsq->shakeCamera(5, t);
  1177 				if (d.damage >= 1)
  1178 				{
  1179 					float shake = d.damage*2;
  1180 					if (shake > 10)
  1181 						shake = 10;
  1182 					dsq->shakeCamera(shake, t);
  1183 				}
  1184 
  1185 				if (healthWillBe <= 2 && d.damageType != DT_ENEMY_ACTIVEPOISON)
  1186 				{
  1187 					//if (!dsq->gameSpeed.isInterpolating() && dsq->gameSpeed.x==1)
  1188 
  1189 					{
  1190 						dsq->gameSpeed.stop();
  1191 						dsq->gameSpeed.stopPath();
  1192 						dsq->gameSpeed.x = 1;
  1193 
  1194 						dsq->overlayRed->alpha.path.clear();
  1195 						dsq->overlayRed->alpha.path.addPathNode(0, 0);
  1196 						dsq->overlayRed->alpha.path.addPathNode(1.0, 0.1);
  1197 						dsq->overlayRed->alpha.path.addPathNode(0, 1.0);
  1198 						dsq->overlayRed->alpha.startPath(1);
  1199 
  1200 						dsq->sound->playSfx("heartbeat");
  1201 
  1202 						if (healthWillBe < 2 && healthWillBe >= 1 && !dsq->game->hasPlayedLow)
  1203 						{
  1204 							dsq->emote.playSfx(EMOTE_NAIJALOW);
  1205 							dsq->game->hasPlayedLow = 1;
  1206 						}
  1207 						
  1208 
  1209 						dsq->gameSpeed.path.clear();
  1210 						
  1211 						dsq->gameSpeed.path.clear();
  1212 						dsq->gameSpeed.path.addPathNode(1, 0);
  1213 						dsq->gameSpeed.path.addPathNode(0.25, 0.1);
  1214 						dsq->gameSpeed.path.addPathNode(0.25, 0.4);
  1215 						dsq->gameSpeed.path.addPathNode(1.0, 1.0);
  1216 
  1217 						dsq->gameSpeed.startPath(2);
  1218 
  1219 						//dsq->gameSpeed.interpolateTo(0.7, 3);
  1220 					}
  1221 					//dsq->emote();
  1222 				}
  1223 			}
  1224 			hitEmitter.load("NaijaHit");
  1225 			hitEmitter.start();
  1226 
  1227 			playHitSound();
  1228 		}
  1229 	}
  1230 }
  1231 
  1232 void Avatar::playHitSound()
  1233 {
  1234 	int hitSound = (rand()%8)+1;
  1235 	static int lastHitSound = 0;
  1236 	if (lastHitSound == hitSound)
  1237 	{
  1238 		hitSound ++;
  1239 		if (hitSound > 8)
  1240 			hitSound = 1;
  1241 	}
  1242 	std::ostringstream os;
  1243 	os << "hit" << hitSound;
  1244 	core->sound->playSfx(os.str());
  1245 }
  1246 
  1247 const int beatHealth = 3;
  1248 void Avatar::updateHeartbeatSfx(float t)
  1249 {
  1250 	/*
  1251 	if (heartbeat)
  1252 	{
  1253 		BASS_CHANNELINFO info;
  1254 		BASS_ChannelGetInfo(heartbeat, &info);
  1255 		int num = (beatHealth - health);
  1256 		float wantFreq = 1000 + num*300;
  1257 		float useFreq = ((wantFreq*info.freq)/1000.0);
  1258 		float vol = 75 + (num*25)*0.5;
  1259 		vol *= (core->sound->getUseSfxVol()/100.0);
  1260 		//int vol = 100;
  1261 		BASS_ChannelSlideAttributes(heartbeat, useFreq, vol, -101, 1000.0*t);
  1262 	}
  1263 	*/
  1264 }
  1265 
  1266 void Avatar::onHealthChange(float change)
  1267 {
  1268 	updateDamageVisualEffects();
  1269 
  1270 	if (health <= beatHealth && health > 0)
  1271 	{
  1272 		/*
  1273 		if (!heartbeat)
  1274 		{
  1275 			//debugLog("starting heartbeat");
  1276 			heartbeat = core->sound->playSfx("Heartbeat", 255, 0, 1000, 1);
  1277 			//core->sound->playSfx("Heartbeat");
  1278 		}
  1279 		*/
  1280 		updateHeartbeatSfx(0.5);
  1281 	}
  1282 	if (health > beatHealth)
  1283 	{
  1284 		/*
  1285 		if (heartbeat)
  1286 		{
  1287 			//debugLog("stopping heartbeat");
  1288 			BASS_CHANNELINFO info;
  1289 			BASS_ChannelGetInfo(heartbeat, &info);
  1290 			BASS_ChannelSlideAttributes(heartbeat, info.freq, -2, -101, 1000*2);
  1291 			heartbeat = 0;
  1292 		}
  1293 		*/
  1294 	}
  1295 }
  1296 
  1297 void Avatar::revive()
  1298 {
  1299 	entityDead	= false;
  1300 	health		= 0;
  1301 	heal(maxHealth);
  1302 }
  1303 
  1304 void Avatar::updateDualFormChargeEffects()
  1305 {
  1306 }
  1307 
  1308 void Avatar::lostTarget(int i, Entity *e)
  1309 {
  1310 	dsq->sound->playSfx("target-unlock");
  1311 }
  1312 
  1313 void Avatar::entityDied(Entity *e)
  1314 {
  1315 	Entity::entityDied(e);
  1316 	for (int i = 0; i < targets.size(); i++)
  1317 	{
  1318 		if (targets[i].e == e)
  1319 		{
  1320 			lostTarget(i, 0);
  1321 			targets[i].e = 0;
  1322 			targetUpdateDelay = 100;
  1323 			targets.clear();
  1324 			break;
  1325 		}
  1326 	}
  1327 
  1328 	if (state.lookAtEntity==e)
  1329 		state.lookAtEntity = 0;
  1330 
  1331 	// eating
  1332 	if (e->isGoingToBeEaten())
  1333 	{
  1334 		EatType et = e->getEatType();
  1335 		switch(et)
  1336 		{
  1337 			case EAT_FILE:
  1338 			{
  1339 				dsq->continuity.eatBeast(e->eatData);
  1340 			}
  1341 			break;
  1342 		}
  1343 	}
  1344 
  1345 	//debugLog("Entity died");
  1346 	//e->lastDamage.damageType == DT_AVATAR_ENERGYBLAST &&
  1347 	//debugLog("Entity died");
  1348 	if (e->lastDamage.form == FORM_DUAL && e->lastDamage.damageType == DT_AVATAR_SHOCK)
  1349 	{
  1350 		dsq->continuity.dualFormCharge ++;
  1351 		updateDualFormChargeEffects();
  1352 		dsq->spawnParticleEffect("SpiritSteal", e->position);
  1353 		//dsq->spawnParticleEffect("SpiritBeacon", position);
  1354 		core->sound->playSfx("DualForm-Absorb");
  1355 		if (dsq->continuity.dualFormCharge == requiredDualFormCharge)
  1356 			core->sound->playSfx("DualForm-Charge");
  1357 	}
  1358 	/*
  1359 	std::ostringstream os;
  1360 	os << "lastDamage.form = " << e->lastDamage.form;
  1361 	debugLog(os.str());
  1362 	*/
  1363 }
  1364 
  1365 void Avatar::enableInput()
  1366 {
  1367 	ActionMapper::enableInput();
  1368 	dsq->game->toggleMiniMapRender(1);
  1369 
  1370 	if (!dsq->game->isApplyingState())
  1371 		dsq->toggleCursor(true);
  1372 
  1373 	if (movingOn)
  1374 	{
  1375 		dsq->setMousePosition(Vector(400,300));
  1376 	}
  1377 
  1378 	if (dsq->continuity.form == FORM_ENERGY)
  1379 	{
  1380 		for (int i = 0; i < targetQuads.size(); i++)
  1381 			targetQuads[i]->start();
  1382 	}
  1383 
  1384 	setInvincible(false);
  1385 	// can't do that here, cause it'll break the hug
  1386 	//stillTimer.stop();
  1387 }
  1388 
  1389 void Avatar::disableInput()
  1390 {
  1391 	ActionMapper::disableInput();
  1392 
  1393 	// can't do that here, cause it'll break the hug
  1394 	//stillTimer.stop();
  1395 
  1396 	closeSingingInterface();
  1397 	dsq->game->toggleMiniMapRender(0);
  1398 	dsq->toggleCursor(false);
  1399 	endCharge();
  1400 	clearTargets();
  1401 	if (movingOn)
  1402 	{
  1403 		dsq->setMousePosition(Vector(400,300));
  1404 	}
  1405 
  1406 	for (int i = 0; i < targetQuads.size(); i++)
  1407 	{
  1408 		targetQuads[i]->stop();
  1409 	}
  1410 
  1411 	setInvincible(true);
  1412 }
  1413 
  1414 void Avatar::clearTargets()
  1415 {
  1416 	for (int i = 0; i < targets.size(); i++)
  1417 	{
  1418 		if (targets[i].e)
  1419 		{
  1420 			lostTarget(i, 0);
  1421 		}
  1422 		targets[i].e = 0;
  1423 	}
  1424 }
  1425 
  1426 void Avatar::slowToRest()
  1427 {
  1428 	vel.capLength2D(50);
  1429 	/*
  1430 	if (vel.getSquaredLength2D() > sqr(50))
  1431 	{
  1432 		vel.setLength2D(50);
  1433 	}
  1434 	*/
  1435 	bursting = swimming = false;
  1436 	skeletalSprite.stopAnimation(1);
  1437 	rotation.interpolateTo(Vector(0,0,0), 0.2, 0, 0, 1);
  1438 }
  1439 
  1440 /*
  1441 #define SPECWIDTH 368
  1442 #define SPECHEIGHT 127
  1443 BYTE *specbuf = 0;
  1444 */
  1445 
  1446 volatile int curMicNote = -1, lastMicNote=-1;
  1447 
  1448 #ifdef BBGE_BUILD_RECORD
  1449 	volatile float timeFromLastNote=0;
  1450 	volatile float timerFreq;
  1451 	volatile float lastLargest=0;
  1452 	volatile bool inMe = false;
  1453 	volatile __int64 lastTick = 0;
  1454 #endif
  1455 
  1456 	/*
  1457 BOOL CALLBACK recordCallback(HRECORD handle, const void *buf, DWORD len, DWORD user)
  1458 {
  1459 
  1460 #ifdef BBGE_BUILD_RECORD
  1461 	if (inMe) return TRUE;
  1462 	inMe = true;
  1463 
  1464 	//if (!dsq->game->avatar->isSinging()) return FALSE;
  1465 	//int x,y;
  1466 
  1467 	//timerFreq =
  1468 	__int64 freq=0;
  1469 	QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
  1470 	__int64 curTime=0;
  1471 	QueryPerformanceCounter((LARGE_INTEGER*)&curTime);
  1472 	if (lastTick == 0)
  1473 	{
  1474 		lastTick = curTime;
  1475 	}
  1476 
  1477 	float fft[4096]; // get the FFT data
  1478 	BASS_ChannelGetData(handle,fft,BASS_DATA_FFT4096);
  1479 
  1480 	int v = 0;
  1481 	int c = 0;
  1482 	float largest = -1;
  1483 
  1484 	//128
  1485 	//for (int i = 5; i < 13; i++)
  1486 	for (int i = 12; i < 64; i++)
  1487 	{
  1488 		if (fft[i] > 0.01 && (fft[i] > largest || largest == -1))
  1489 		{
  1490 			largest = fft[i];
  1491 			v = i;
  1492 		}
  1493 	}
  1494 
  1495 	int v2=0;
  1496 	largest = -1;
  1497 	// find the next largest
  1498 
  1499 	float dt = (double(curTime-lastTick)/double(freq));
  1500 	lastTick = curTime;
  1501 	int posMicNote=-1;
  1502 	if (c != 0)
  1503 		v /= float(c);
  1504 	int ov = v;
  1505 	float factor=1.0;
  1506 	int octave = dsq->user.audio.octave;
  1507 
  1508 	int minNote = 12;///6;
  1509 	int maxNote = minNote + 25; // 8
  1510 	int octRange = 11;
  1511 
  1512 	minNote += (octRange)*octave;
  1513 	maxNote += (octRange)*octave;
  1514 
  1515 	posMicNote = dsq->fftnotes.getNoteFromFFT(v, octave);
  1516 
  1517 	//if (lastLargest<largest || fabs(largest-lastLargest) < 0.05)
  1518 	if (true)
  1519 	{
  1520 		lastLargest = largest;
  1521 
  1522 		float closeRange = 0.05;
  1523 		// check for our e natural
  1524 
  1525 		if (posMicNote != -1)
  1526 		{
  1527 
  1528 			curMicNote = posMicNote;
  1529 		}
  1530 		else
  1531 		{
  1532 			curMicNote = -1;
  1533 		}
  1534 	}
  1535 	else
  1536 	{
  1537 		lastLargest -= dt;
  1538 	}
  1539 
  1540 
  1541 	timeFromLastNote += dt;
  1542 	if (curMicNote != lastMicNote)
  1543 		timeFromLastNote = 0;
  1544 	if (timeFromLastNote > 0.0001)
  1545 	{
  1546 		micNote = curMicNote;
  1547 		timeFromLastNote = 0;
  1548 	}
  1549 	if (curMicNote == -1)
  1550 		micNote = -1;
  1551 	lastMicNote = curMicNote;
  1552 
  1553 	inMe = false;
  1554 #endif
  1555 	return TRUE;
  1556 }
  1557 */
  1558 
  1559 /*
  1560 
  1561 class FoodIcon2 : public Quad
  1562 {
  1563 };
  1564 
  1565 class FoodIcon : public Quad
  1566 {
  1567 public:
  1568 	FoodIcon(IngredientEffectType iet);
  1569 
  1570 	IngredientEffectType type;
  1571 
  1572 	void 
  1573 };
  1574 
  1575 void Avatar::openFoodInterface()
  1576 {
  1577 	if (!singing && !pickingPullTarget && health > 0 && !isEntityDead() && !blockSinging)
  1578 	{
  1579 		// build it
  1580 		foodIcons.clear();
  1581 
  1582 		foodIcons.resize(8);
  1583 	}
  1584 }
  1585 */
  1586 
  1587 void Avatar::openSingingInterface()
  1588 {
  1589 	if (!singing && !pickingPullTarget && health > 0 && !isEntityDead() && !blockSinging)
  1590 	{
  1591 		//core->mouse.position = Vector(400,300);
  1592 		if (dsq->inputMode != INPUT_MOUSE)
  1593 		{
  1594 			core->centerMouse();
  1595 			//core->setMousePosition(Vector(400,300));
  1596 		}
  1597 
  1598 		core->setMouseConstraintCircle(singingInterfaceRadius);
  1599 		stopRoll();
  1600 		singing = true;
  1601 		currentSongIdx = SONG_NONE;
  1602 
  1603 		// make the singing icons appear
  1604 		for (int i = 0; i < songIcons.size(); i++)
  1605 		{
  1606 			songIcons[i]->openInterface();
  1607 		}
  1608 		currentSong.notes.clear();
  1609 
  1610 		//
  1611 		songInterfaceTimer = 0;
  1612 
  1613 		dsq->game->songLineRender->clear();
  1614 
  1615 		//if (avatarRecord)
  1616 		//{
  1617 		//	if (dsq->useMic && !dsq->autoSingMenuOpen && dsq->user.audio.micOn)
  1618 		//	{
  1619 		//		//avatarRecord=BASS_RecordStart(44100,1,0,&recordCallback,0);
  1620 		//		BASS_ChannelPlay(avatarRecord, false);
  1621 		//	}
  1622 		//}
  1623 
  1624 		if (dsq->inputMode == INPUT_JOYSTICK)
  1625 		{
  1626 			core->setMousePosition(core->center);
  1627 		}
  1628 	}
  1629 }
  1630 
  1631 void Avatar::closeSingingInterface()
  1632 {
  1633 
  1634 	if (dsq->game->songLineRender)
  1635 		dsq->game->songLineRender->clear();
  1636 	if (singing)
  1637 	{
  1638 		core->setMouseConstraint(false);
  1639 		usingDigital = false;
  1640 		quickSongCastDelay = 1;
  1641 
  1642 		// HACK: this prevents being "locked" away from the seahorse... so naija can
  1643 		// be in singing range of the seahorse
  1644 		applyRidingPosition();
  1645 		singing = false;
  1646 
  1647 		for (int i = 0; i < songIcons.size(); i++)
  1648 		{
  1649 			songIcons[i]->closeInterface();
  1650 		}
  1651 
  1652 		if (dsq->continuity.form == FORM_NORMAL)
  1653 			setHeadTexture("");
  1654 
  1655 		currentSongIdx = dsq->continuity.checkSongAssisted(currentSong);
  1656 		if (currentSongIdx != SONG_NONE)
  1657 		{
  1658 			dsq->continuity.castSong(currentSongIdx);
  1659 			currentSongIdx = SONG_NONE;
  1660 		}
  1661 
  1662 		/*
  1663 		if (avatarRecord)
  1664 		{
  1665 			if (dsq->useMic && !dsq->autoSingMenuOpen && dsq->user.audio.micOn)
  1666 			{
  1667 				//BASS_ChannelStop(avatarRecord);
  1668 				BASS_ChannelPause(avatarRecord);
  1669 				//BASS_RecordFree();
  1670 				//avatarRecord = 0;
  1671 
  1672 			}
  1673 		}
  1674 		*/
  1675 
  1676 		lastMicNote = curMicNote = micNote = -1;
  1677 	}
  1678 }
  1679 
  1680 void Avatar::openFormInterface()
  1681 {
  1682 	if (!inFormInterface)
  1683 	{
  1684 		inFormInterface = true;
  1685 
  1686 		for(int i = 0; i < formIcons.size(); i++)
  1687 		{
  1688 			formIcons[i]->alpha.interpolateTo(1, 0.1);
  1689 		}
  1690 	}
  1691 }
  1692 
  1693 void Avatar::closeFormInterface()
  1694 {
  1695 	if (inFormInterface)
  1696 	{
  1697 		inFormInterface = false;
  1698 
  1699 		for (int i = 0; i < formIcons.size(); i++)
  1700 		{
  1701 			formIcons[i]->alpha.interpolateTo(0, 0.1);
  1702 		}
  1703 	}
  1704 }
  1705 
  1706 void Avatar::toggleCape(bool on)
  1707 {
  1708 	if (!hair) return;
  1709 
  1710 	if (!on)
  1711 		hair->alphaMod = 0;
  1712 	else
  1713 		hair->alphaMod = 1;
  1714 }
  1715 
  1716 void Avatar::refreshDualFormModel()
  1717 {
  1718 	//charging = 0;
  1719 	if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
  1720 		refreshModel("Naija", "DualForm_Naija");
  1721 	else if (dsq->continuity.dualFormMode == Continuity::DUALFORM_LI)
  1722 		refreshModel("Naija", "DualForm_Li");
  1723 }
  1724 
  1725 void Avatar::updateDualFormGlow(float dt)
  1726 {
  1727 	if (dsq->continuity.form == FORM_DUAL && bone_dualFormGlow)
  1728 	{
  1729 
  1730 		float perc = 1;
  1731 		if (requiredDualFormCharge != 0)
  1732 			perc = float(dsq->continuity.dualFormCharge)/float(requiredDualFormCharge);
  1733 		if (perc > 1)
  1734 			perc = 1;
  1735 		bone_dualFormGlow->alpha = perc*0.5 + 0.1;
  1736 		bone_dualFormGlow->scale.interpolateTo(Vector(perc, perc), 0.2);
  1737 	}
  1738 }
  1739 
  1740 void Avatar::changeForm(FormType form, bool effects, bool onInit, FormType lastForm)
  1741 {
  1742 	/*
  1743 	if (core->afterEffectManager)
  1744 		core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter, 0.1,0.03,15,0.2f, 0.5));
  1745 	*/
  1746 
  1747 
  1748 	/*
  1749 	if (pullTarget)
  1750 	{
  1751 		pullTarget->stopPull();
  1752 		pullTarget = 0;
  1753 	}
  1754 	*/
  1755 
  1756 	if (form == FORM_DUAL && !dsq->continuity.hasLi())
  1757 		return;
  1758 
  1759 	if (!canChangeForm) return;
  1760 
  1761 	std::ostringstream os;
  1762 	os << "changeForm: " << form;
  1763 	debugLog(os.str());
  1764 
  1765 	/*
  1766 	if (dsq->game)
  1767 		dsq->game->clearControlHint();
  1768 	*/
  1769 
  1770 	if (lastForm == FORM_NONE)
  1771 		lastForm = dsq->continuity.form;
  1772 
  1773 	endCharge();
  1774 
  1775 	std::ostringstream os2;
  1776 	os2 << "lastForm: " << lastForm;
  1777 	debugLog(os2.str());
  1778 
  1779 	for (int i = 0; i < targetQuads.size(); i++)
  1780 	{
  1781 		if (targetQuads[i])
  1782 			targetQuads[i]->stop();
  1783 	}
  1784 
  1785 
  1786 	if (bone_dualFormGlow)
  1787 		bone_dualFormGlow->scale = 0;
  1788 
  1789 	clearTargets();
  1790 
  1791 	if (form != FORM_NORMAL)
  1792 		stopAura();
  1793 
  1794 	switch (lastForm)
  1795 	{
  1796 	case FORM_FISH:
  1797 	{
  1798 		// check nearby area
  1799 		//bool isTooCloseToWall=false;
  1800 
  1801 		if (isNearObstruction(3))
  1802 		{
  1803 			Vector n = dsq->game->getWallNormal(position);
  1804 			if (!n.isZero())
  1805 			{
  1806 				n *= 400;
  1807 				vel += n;
  1808 			}
  1809 
  1810 			return;
  1811 		}
  1812 		//rotationOffset.interpolateTo(Vector(0,0,0), 0.5);
  1813 	}
  1814 	break;
  1815 	case FORM_SUN:
  1816 		lightFormGlow->alpha.interpolateTo(0, 0.5);
  1817 		lightFormGlowCone->alpha.interpolateTo(0, 0.5);
  1818 	break;
  1819 	case FORM_SPIRIT:
  1820 		//position.interpolateTo(bodyPosition, 2, 0);
  1821 		position = bodyPosition;
  1822 		dsq->continuity.warpLiToAvatar();
  1823 		spiritBeaconEmitter.start();
  1824 	break;
  1825 	case FORM_BEAST:
  1826 		//dsq->game->sceneColor3.interpolateTo(Vector(1, 1, 1), 0.5);
  1827 	break;
  1828 	case FORM_DUAL:
  1829 		if (dsq->continuity.hasLi())
  1830 		{
  1831 			dsq->game->li->alpha = 1;
  1832 			dsq->game->li->position = position;
  1833 			dsq->game->li->setState(STATE_IDLE);
  1834 		}
  1835 	break;
  1836 	default:
  1837 		if (leftHandEmitter && rightHandEmitter)
  1838 		{
  1839 			leftHandEmitter->stop();
  1840 			rightHandEmitter->stop();
  1841 		}
  1842 	break;
  1843 	}
  1844 
  1845 
  1846 	state.abilityDelay = 0;
  1847 	formAbilityDelay = 0;
  1848 	dsq->continuity.form = form;
  1849 	ropeState = 0;
  1850 	formTimer = 0;
  1851 	if (effects)
  1852 	{
  1853 		if (core->afterEffectManager)
  1854 			core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
  1855 
  1856 		switch(form)
  1857 		{
  1858 		case FORM_ENERGY:
  1859 			core->sound->playSfx("EnergyForm");
  1860 			/*
  1861 			dsq->game->tintColor.path.addPathNode(Vector(1,1,1),0);
  1862 			dsq->game->tintColor.path.addPathNode(Vector(1.5,1.5,4),0.25);
  1863 			dsq->game->tintColor.path.addPathNode(Vector(4,1.5,1),0.5);
  1864 			dsq->game->tintColor.path.addPathNode(Vector(1,1,1),0.5);
  1865 			dsq->game->tintColor.startPath(2);
  1866 			*/
  1867 
  1868 			/*
  1869 			dsq->game->tintColor = Vector(1,1,3);
  1870 			dsq->game->tintColor.interpolateTo(Vector(1,1,1), 1);
  1871 			*/
  1872 
  1873 		break;
  1874 		case FORM_NORMAL:
  1875 			core->sound->playSfx("NormalForm");
  1876 		break;
  1877 		case FORM_BEAST:
  1878 			core->sound->playSfx("BeastForm");
  1879 		break;
  1880 		case FORM_FISH:
  1881 			core->sound->playSfx("FishForm");
  1882 		break;
  1883 		case FORM_SUN:
  1884 			core->sound->playSfx("SunForm");
  1885 		break;
  1886 		case FORM_NATURE:
  1887 			core->sound->playSfx("NatureForm");
  1888 		break;
  1889 		case FORM_SPIRIT:
  1890 			spiritBeaconEmitter.start();
  1891 		break;
  1892 		case FORM_DUAL:
  1893 			core->sound->playSfx("DualForm");
  1894 		break;
  1895 		}
  1896 
  1897 		/*
  1898 		dsq->overlay->color = Vector(1,1,1);
  1899 		dsq->overlay->alpha.interpolateTo(1.0, 0.2);
  1900 		avatar->disableInput();
  1901 		setv(EV_NOINPUTNOVEL, 0);
  1902 		core->main(0.2);
  1903 		setv(EV_NOINPUTNOVEL, 1);
  1904 		dsq->overlay->alpha.interpolateTo(0, 0.2);
  1905 		dsq->overlay->color.interpolateTo(0, 0.4);
  1906 		*/
  1907 		dsq->overlay->color = Vector(1,1,1);
  1908 		dsq->overlay->alpha = 1;
  1909 		dsq->overlay->alpha.interpolateTo(0, 0.5);
  1910 		dsq->overlay->color.interpolateTo(0, 1.0);
  1911 	}
  1912 	/*
  1913 	if (form != FORM_ENERGY)
  1914 	{
  1915 		dsq->game->sceneColor3.interpolateTo(Vector(1,1,1), 0.2);
  1916 	}
  1917 	*/
  1918 	float lastHairAlphaMod = 0;
  1919 	if (hair)
  1920 	{
  1921 		hair->alphaMod = 0;
  1922 		lastHairAlphaMod = hair->alphaMod;
  1923 	}
  1924 	switch (form)
  1925 	{
  1926 	case FORM_ENERGY:
  1927 		refreshModel("Naija", "EnergyForm");
  1928 		for (int i = 0; i < targetQuads.size(); i++)
  1929 			targetQuads[i]->start();
  1930 		leftHandEmitter->load("EnergyFormHandGlow");
  1931 		leftHandEmitter->start();
  1932 		rightHandEmitter->load("EnergyFormHandGlow");
  1933 		rightHandEmitter->start();
  1934 	break;
  1935 	case FORM_FISH:
  1936 	{
  1937 		fallOffWall();
  1938 
  1939 		setBoneLock(BoneLock());
  1940 
  1941 		refreshModel("FishForm", "");
  1942 		//rotationOffset.interpolateTo(Vector(0,0,-90), 0.5);
  1943 		//refreshModel("NaijaFish", "");
  1944 	}
  1945 	break;
  1946 	case FORM_SUN:
  1947 	{
  1948 		refreshModel("Naija", "SunForm");
  1949 		lightFormGlow->moveToFront();
  1950 		lightFormGlow->alpha.interpolateTo(0.75, 1);
  1951 		lightFormGlowCone->alpha.interpolateTo(0.4, 1);
  1952 
  1953 		lightFormGlow->alphaMod = 0;
  1954 		lightFormGlowCone->alphaMod = 0;
  1955 	}
  1956 	break;
  1957 	case FORM_NORMAL:
  1958 	{
  1959 		if (lastForm == FORM_SPIRIT)
  1960 		{
  1961 			dsq->continuity.shiftWorlds();
  1962 			fallOffWall();
  1963 		}
  1964 		refreshNormalForm();
  1965 		//skeletalSprite.loadSkeletal("child");
  1966 	}
  1967 	break;
  1968 	case FORM_NATURE:
  1969 		refreshModel("Naija", "NatureForm");
  1970 		if (hair)
  1971 		{
  1972 			hair->setTexture("Naija/Cape-NatureForm");
  1973 			hair->alphaMod = 1.0;
  1974 		}
  1975 		/*
  1976 		skeletalSprite.loadSkin("ChildTeira");
  1977 		refreshModel();
  1978 		*/
  1979 		/*
  1980 		if (dsq->game->sceneNatureForm == "forest")
  1981 		{
  1982 			debugLog("Forest Form");
  1983 			dsq->continuity.form = FORM_NATURE_FOREST;
  1984 		}
  1985 		else if (dsq->game->sceneNatureForm == "sun")
  1986 		{
  1987 			dsq->continuity.form = FORM_NATURE_SUN;
  1988 			debugLog("Sun Form");
  1989 		}
  1990 		else if (dsq->game->sceneNatureForm == "fire")
  1991 		{
  1992 			dsq->continuity.form = FORM_NATURE_FIRE;
  1993 			debugLog("Fire Form");
  1994 		}
  1995 		else if (dsq->game->sceneNatureForm == "dark")
  1996 		{
  1997 			dsq->continuity.form = FORM_NATURE_DARK;
  1998 			debugLog("Dark Form");
  1999 		}
  2000 		else if (dsq->game->sceneNatureForm == "rock" || dsq->game->sceneNatureForm.empty())
  2001 		{
  2002 			dsq->continuity.form = FORM_NATURE_ROCK;
  2003 			debugLog("Rock Form");
  2004 		}
  2005 		*/
  2006 
  2007 	break;
  2008 	case FORM_BEAST:
  2009 	{
  2010 		refreshModel("Naija", "BeastForm");
  2011 	}
  2012 	break;
  2013 	case FORM_SPIRIT:
  2014 		bodyPosition = position;
  2015 		bodyOffset = offset;
  2016 		fallOffWall();
  2017 		dsq->continuity.shiftWorlds();
  2018 
  2019 		if (onInit)
  2020 		{
  2021 			skeletalSprite.alphaMod = 0;
  2022 			canChangeForm = false;
  2023 			useSpiritDistance = false;
  2024 			inSpiritWorld = true;
  2025 		}
  2026 		/*
  2027 		if (hair)
  2028 			hair->alphaMod = lastHairAlphaMod;
  2029 		*/
  2030 	break;
  2031 	case FORM_DUAL:
  2032 	{
  2033 		if (dsq->continuity.hasLi())
  2034 		{
  2035 			dsq->game->li->setState(STATE_WAIT);
  2036 			dsq->game->li->alpha = 0;
  2037 		}
  2038 		//dualFormMode = DUALFORM_LI;
  2039 		refreshDualFormModel();
  2040 		/*
  2041 		for (int i = 0; i < targetQuads.size(); i++)
  2042 			targetQuads[i]->start();
  2043 		*/
  2044 	}
  2045 	break;
  2046 	default:
  2047 	break;
  2048 	}
  2049 	setHeadTexture("");
  2050 	if (effects)
  2051 		avatar->enableInput();
  2052 
  2053 	//if (onInit) {
  2054 		//idle();//skeletalSprite.animate("idle", -1, 0);
  2055 	//}
  2056 }
  2057 
  2058 int Avatar::getLastNote()
  2059 {
  2060 	return lastNote;
  2061 }
  2062 
  2063 void Avatar::singNote(int note)
  2064 {
  2065 	currentSong.notes.push_back(note);
  2066 	lastNote = note;
  2067 	//int song = dsq->continuity.checkSong(currentSong);
  2068 	//int song = dsq->continuity.checkSongAssisted(currentSong);
  2069 	//int song = dsq->continuity.checkSongAssisted(currentSong);
  2070 	/*
  2071 	std::ostringstream os;
  2072 	os << "sung note: " << note;
  2073 	debugLog(os.str());
  2074 	*/
  2075 	//currentSongIdx = song;
  2076 	/*
  2077 	if (song != -1)
  2078 	{
  2079 	*/
  2080 		/*
  2081 		std::ostringstream os;
  2082 		os << "Sung Song: " << song;
  2083 		debugLog(os.str());
  2084 		*/
  2085 		// close in a few seconds
  2086 		//closeSingingInterface();
  2087 	//}
  2088 }
  2089 
  2090 void Avatar::updateSingingInterface(float dt)
  2091 {
  2092 
  2093 	//if (singing)
  2094 	if (songIcons.size()>0 && songIcons[0]->alpha.x > 0)
  2095 	{
  2096 		if (dsq->inputMode != INPUT_JOYSTICK && !core->mouse.change.isZero())
  2097 		{
  2098 			if (dsq->game->songLineRender && songIcons[0]->alpha.x == 1)
  2099 			{
  2100 				int smallestDist = -1;
  2101 				int closest =-1;
  2102 				for (int i = 0; i < songIcons.size(); i++)
  2103 				{
  2104 					int dist = (songIcons[i]->position - core->mouse.position).getSquaredLength2D();
  2105 					if (smallestDist == -1 || dist < smallestDist)
  2106 					{
  2107 						smallestDist = dist;
  2108 						closest = i;
  2109 					}
  2110 				}
  2111 
  2112 				dsq->game->songLineRender->newPoint(core->mouse.position, songIcons[closest]->noteColor);
  2113 			}
  2114 		}
  2115 
  2116 		if (health <= 0 || isEntityDead())
  2117 		{
  2118 			closeSingingInterface();
  2119 		}
  2120 		else
  2121 		{
  2122 			//if (dsq->inputMode == INPUT_JOYSTICK)
  2123 			{
  2124 				//core->mouse.position += core->joystick.position * dsq->user.control.joyCursorSpeed;
  2125 
  2126 				/*
  2127 				cursorPos.update(dt);
  2128 				if (cursorPos.isInterpolating())
  2129 				{
  2130 				}
  2131 				*/
  2132 
  2133 				int cursorRadius = singingInterfaceRadius-8;
  2134 
  2135 
  2136 
  2137 				static float returnDelay = 0;
  2138 
  2139 				Vector desired;
  2140 
  2141 				/*
  2142 				// was recently in...
  2143 				if (core->joystick.dpadLeft || isActing(ACTION_SWIMLEFT))
  2144 				{
  2145 					//debugLog("left");
  2146 					desired.x = -(1);
  2147 					usingDigital = true;
  2148 				}
  2149 				if (core->joystick.dpadRight || isActing(ACTION_SWIMRIGHT))
  2150 				{
  2151 					//debugLog("right");
  2152 					desired.x = (1);
  2153 					usingDigital = true;
  2154 				}
  2155 
  2156 				if (core->joystick.dpadDown || isActing(ACTION_SWIMDOWN))
  2157 				{
  2158 					//debugLog("down");
  2159 					desired.y = 1;
  2160 					usingDigital = true;
  2161 				}
  2162 				if (core->joystick.dpadUp || isActing(ACTION_SWIMUP))
  2163 				{
  2164 					//debugLog("up");
  2165 					desired.y = -1;
  2166 					usingDigital = true;
  2167 				}
  2168 				*/
  2169 
  2170 				/*
  2171 				static float dpadTimer = 0;
  2172 				static Vector lastDesired;
  2173 				bool doit = false;
  2174 				if (desired == lastDesired)
  2175 				{
  2176 					dpadTimer += dt;
  2177 					if (dpadTimer > 0.05)
  2178 					{
  2179 						dpadTimer = 0;
  2180 						doit = true;
  2181 					}
  2182 				}
  2183 				else
  2184 				{
  2185 					dpadTimer = 0;
  2186 				}
  2187 
  2188 				if (doit)
  2189 				{
  2190 					desired.setLength2D(cursorRadius);
  2191 					core->mouse.position = Vector(400,300)+desired;
  2192 					core->setMousePosition(core->mouse.position);
  2193 
  2194 				}
  2195 				lastDesired = desired;
  2196 				*/
  2197 
  2198 				//desired.setLength2D(cursorRadius);
  2199 
  2200 				returnDelay -= dt;
  2201 
  2202 				int spd = 1500;//850;
  2203 
  2204 				desired.setLength2D(spd*dt);
  2205 
  2206 				
  2207 				if (dsq->inputMode == INPUT_JOYSTICK)// && !dsq->joystick.position.isLength2DIn(0.4))
  2208 				{
  2209 
  2210 					//if (!dsq->joystick.position.isLength2DIn(0.9))
  2211 					{
  2212 						Vector p(core->center);
  2213 						Vector d = dsq->joystick.position;
  2214 
  2215 						/*
  2216 						d.x = float(int((d.x*2)))/4.0;
  2217 						d.y = float(int((d.y*2)))/4.0;
  2218 						d.normalize2D();
  2219 						*/
  2220 
  2221 						if (!d.isLength2DIn(0.6))
  2222 						{
  2223 							d.normalize2D();
  2224 						}
  2225 
  2226 						p = p + d*cursorRadius-1;
  2227 						core->setMousePosition(p);
  2228 					}
  2229 				}
  2230 				else if (desired.x != 0 || desired.y != 0)
  2231 				{
  2232 					returnDelay = 0.1;
  2233 					//debugLog("desired not zero");
  2234 					//debugLog("mouse position set");
  2235 					//core->mouse.position = Vector(400,300)+desired;
  2236 					core->mouse.position += desired;
  2237 
  2238 					Vector diff = core->mouse.position - Vector(400,300);
  2239 					if (!diff.isLength2DIn(cursorRadius))
  2240 					{
  2241 						diff.setLength2D(cursorRadius);
  2242 						core->mouse.position = Vector(400,300) +diff;
  2243 					}
  2244 					core->setMousePosition(core->mouse.position);
  2245 					/*
  2246 					cursorPos.interpolateTo(desired, 0.1);
  2247 					core->setMousePosition(cursorPos);
  2248 					*/
  2249 				}
  2250 				else if (usingDigital)
  2251 				{
  2252 					if (returnDelay <= 0)
  2253 					{
  2254 						debugLog("desired is zero");
  2255 						Vector c = core->center;
  2256 						Vector dir = c - core->mouse.position;
  2257 						if (dir.isLength2DIn(8))
  2258 						{
  2259 							debugLog("in range");
  2260 							core->mouse.position = c;
  2261 						}
  2262 						else
  2263 						{
  2264 							debugLog("NOT in range");
  2265 							dir.setLength2D(dt * 400);
  2266 							core->mouse.position += dir;
  2267 						}
  2268 						core->setMousePosition(core->mouse.position);
  2269 					}
  2270 				}
  2271 
  2272 
  2273 
  2274 
  2275 
  2276 					//(core->joystick.position * (singingInterfaceRadius-16)) + Vector(400,300);
  2277 			}
  2278 
  2279 			//Vector dist = dsq->getGameCursorPosition() - dsq->game->avatar->position;
  2280 			/*
  2281 			Vector dist = core->mouse.position - Vector(400,300);
  2282 			int checkRad = singingInterfaceRadius-10;
  2283 			if (dist.getSquaredLength2D() > sqr(checkRad))
  2284 			{
  2285 				dist |= (checkRad);
  2286 				core->setMousePosition(Vector(400,300)+dist);
  2287 			}
  2288 			*/
  2289 
  2290 			/*
  2291 			static float timer=0;
  2292 			timer += dt;
  2293 			if (timer > 0.1)
  2294 			{
  2295 				if (dist.getSquaredLength2D() > sqr(singingInterfaceRadius))
  2296 				{
  2297 					dist |= singingInterfaceRadius-10;
  2298 					//core->mouse.position = dsq->game->avatar->position + dist;
  2299 
  2300 				}
  2301 			}
  2302 			*/
  2303 			//core->setMousePosition(Vector(400,300)+dist/*dsq->game->avatar->position + dist - core->screenCenter*/);
  2304 			//HACK: constrain the mouse to the circle
  2305 			/*
  2306 			if (dist.getSquaredLength2D() > sqr(singingInterfaceRadius*core->invGlobalScale))//*core->invGlobalScale))
  2307 			{
  2308 
  2309 				dist |= singingInterfaceRadius*core->invGlobalScale-20;
  2310 				//core->setMousePosition(Vector(400,300));
  2311 				core->setMousePosition(((dsq->game->avatar->position + dist)-dsq->screenCenter) + Vector(400,300));
  2312 				//core->setMousePosition(dsq->game->avatar->position + dist);
  2313 				//core->setMousePosition((dsq->game->avatar->position + dist)*core->globalScale.x - dsq->screenCenter);
  2314 			}
  2315 			*/
  2316 
  2317 			setSongIconPositions();
  2318 		}
  2319 	}
  2320 
  2321 }
  2322 
  2323 void Avatar::setSongIconPositions()
  2324 {
  2325 	float radIncr = (2*PI)/float(songIcons.size());
  2326 	float rad = 0;
  2327 	for (int i = 0; i < songIcons.size(); i++)
  2328 	{
  2329 		songIcons[i]->position = Vector(400,300)+/*this->position + */Vector(sin(rad)*singingInterfaceRadius, cos(rad)*singingInterfaceRadius);
  2330 		rad += radIncr;
  2331 	}
  2332 }
  2333 
  2334 const int chkDist = 2500*2500;
  2335 
  2336 Target Avatar::getNearestTarget(const Vector &checkPos, const Vector &distPos, Entity *source, DamageType dt, bool override, std::vector<Target> *ignore, EntityList *entityList)
  2337 {
  2338 	BBGE_PROF(Avatar_getNearestTarget);
  2339 	if (!entityList)
  2340 		entityList = &dsq->entities;
  2341 	Target t;
  2342 
  2343 	Vector targetPosition;
  2344 	int targetPt = -1;
  2345 	Entity *closest = 0;
  2346 	int highestPriority = -999;
  2347 	int smallestDist = -1;
  2348 	Entity *e = 0;
  2349 	FOR_ENTITIES(i)
  2350 	{
  2351 		e = *i;
  2352 		/*
  2353 		int j;
  2354 		for (j = 0; j < targets.size(); j++)
  2355 		{
  2356 			if (targets[j].e == e) break;
  2357 		}
  2358 		if (j != targets.size()) continue;
  2359 		*/
  2360 
  2361 		//e &&
  2362 		if (e != this && e->targetPriority >= highestPriority && this->pullTarget != e && e->isDamageTarget(dt) && dsq->game->isValidTarget(e, this))
  2363 		{
  2364 
  2365 
  2366 
  2367 			if (e->position.isNan())
  2368 			//if (false)
  2369 			{
  2370 				std::ostringstream os;
  2371 				os << "NAN position entity name: " << e->name << " type: " << e->getEntityType();
  2372 				debugLog(os.str());
  2373 				continue;
  2374 			}
  2375 			else
  2376 			{
  2377 				int dist = (e->position - position).getSquaredLength2D();
  2378 				if (dist < chkDist)
  2379 				{
  2380 					int numTargetPoints = e->getNumTargetPoints();
  2381 					bool clearAfter = false;
  2382 					if (numTargetPoints == 0)
  2383 					{
  2384 						if (ignore)
  2385 						{
  2386 							int j = 0;
  2387 							for (; j < ignore->size(); j++)
  2388 							{
  2389 								if ((*ignore)[j].e == e)
  2390 									break;
  2391 							}
  2392 							if (j != ignore->size()) continue;
  2393 						}
  2394 						e->addTargetPoint(e->getEnergyShotTargetPosition());
  2395 						clearAfter = true;
  2396 						numTargetPoints = 1;
  2397 					}
  2398 					if (numTargetPoints > 0)
  2399 					{
  2400 						for (int i = 0; i < numTargetPoints; i++)
  2401 						{
  2402 							if (ignore)
  2403 							{
  2404 								int j = 0;
  2405 								for (; j < ignore->size(); j++)
  2406 								{
  2407 									if ((*ignore)[j].e == e && (*ignore)[j].targetPt == i)
  2408 										break;
  2409 								}
  2410 								if (j != ignore->size()) continue;
  2411 							}
  2412 							int dist = (e->getTargetPoint(i) - distPos).getSquaredLength2D();
  2413 							//int dist = (e->getTargetPoint(i) - distPos).getLength2D();
  2414 							if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
  2415 							{
  2416 								if (override || (checkPos - e->getTargetPoint(i)).isLength2DIn(64))
  2417 								{
  2418 									dist = (e->getTargetPoint(i) - checkPos).getSquaredLength2D();
  2419 									if (smallestDist == -1 || dist < smallestDist)
  2420 									{
  2421 										highestPriority = e->targetPriority;
  2422 										targetPosition = e->getTargetPoint(i);
  2423 										closest = e;
  2424 										smallestDist = dist;
  2425 										targetPt = i;
  2426 									}
  2427 								}
  2428 							}
  2429 						}
  2430 					}
  2431 					if (clearAfter)
  2432 						e->clearTargetPoints();
  2433 				}
  2434 			}
  2435 		}
  2436 	}
  2437 	t.e = closest;
  2438 	t.pos = targetPosition;
  2439 	t.targetPt = targetPt;
  2440 	return t;
  2441 }
  2442 
  2443 float maxTargetDelay = 0.5;
  2444 bool wasDown = false;
  2445 void Avatar::updateTargets(float dt, bool override)
  2446 {
  2447 	DamageType damageType = DT_AVATAR_ENERGYBLAST;
  2448 	for (int i = 0; i < targets.size(); i++)
  2449 	{
  2450 		if (!targets[i].e
  2451 		|| !targets[i].e->isPresent()
  2452 		|| targets[i].e->getState() == STATE_DEATHSCENE
  2453 		|| !dsq->game->isValidTarget(targets[i].e, this))
  2454 		{
  2455 			targets.clear();
  2456 			break;
  2457 		}
  2458 	}
  2459 	if ((dsq->inputMode == INPUT_MOUSE || dsq->inputMode == INPUT_KEYBOARD) && !(wasDown && core->mouse.buttons.right))
  2460 	{
  2461 		wasDown = false;
  2462 		float mod = 1;
  2463 		if (isCharging())
  2464 			mod = maxTargetDelay*10;
  2465 		targetUpdateDelay += dt*mod;
  2466 	}
  2467 
  2468 	if (targetUpdateDelay > maxTargetDelay || override)
  2469 	{
  2470 		maxTargetDelay = 0;
  2471 		std::vector<Target> oldTargets = targets;
  2472 		if ((dsq->continuity.form == FORM_ENERGY) && ((core->mouse.buttons.right && state.spellCharge > 0.3) || override))
  2473 			//&& state.spellCharge > 0.2 /*&& state.spellCharge < 0.5*/
  2474 		{
  2475 			// crappy hack for now, assuming one target:
  2476 			targets.clear();
  2477 
  2478 			
  2479 			Vector dir = getAim();
  2480 			Vector checkPos = position + dir;
  2481 			Vector distPos = position;
  2482 			if (!(dsq->getGameCursorPosition() - distPos).isLength2DIn(4))
  2483 			{
  2484 				Target t;
  2485 				t = getNearestTarget(checkPos, distPos, this, damageType, override, &targets);
  2486 				if (t.e)
  2487 				{
  2488 					//if ((t.getWorldPosition() - dsq->getGameCursorPosition()).isLength2DIn(64))
  2489 					{
  2490 
  2491 
  2492 						// found a target?
  2493 						targets.push_back(t);
  2494 
  2495 						targetUpdateDelay = 0;
  2496 						if (!override && core->mouse.buttons.right)
  2497 						{
  2498 							maxTargetDelay = 90;
  2499 							
  2500 							dsq->spawnParticleEffect("TargetAquired", t.pos);
  2501 							wasDown = true;
  2502 						}
  2503 					}
  2504 				}
  2505 			}
  2506 			if (targets.empty())
  2507 			{
  2508 				for (int i = 0; i < oldTargets.size(); i++)
  2509 				{
  2510 					Entity *e = oldTargets[i].e;
  2511 					if (e)
  2512 					{
  2513 						int dist = (e->getTargetPoint(oldTargets[i].targetPt) - distPos).getSquaredLength2D();
  2514 						if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
  2515 						{
  2516 							targets.push_back(oldTargets[i]);
  2517 						}
  2518 					}
  2519 					else
  2520 					{
  2521 						targets.clear();
  2522 						break;
  2523 					}
  2524 				}
  2525 			}
  2526 		}
  2527 	}
  2528 	else
  2529 	{
  2530 		for (int i = 0; i < targets.size(); i++)
  2531 		{
  2532 			Entity *e = targets[i].e;
  2533 			if (e)
  2534 			{
  2535 				if (!(position - e->position).isLength2DIn(e->getTargetRange() + TARGET_RANGE + TARGET_GRACE_RANGE) || !dsq->game->isValidTarget(e, this) || !e->isDamageTarget(damageType))
  2536 				{
  2537 					lostTarget(i, targets[i].e);
  2538 					targets[i].e = 0;
  2539 					targetUpdateDelay = maxTargetDelay;
  2540 					wasDown = false;
  2541 				}
  2542 			}
  2543 		}
  2544 	}
  2545 }
  2546 
  2547 void Avatar::loseTargets()
  2548 {
  2549 	for (int i = 0; i < targets.size(); i++)
  2550 	{
  2551 		Entity *e = targets[i].e;
  2552 		if (e)
  2553 		{
  2554 			lostTarget(i, targets[i].e);
  2555 			targets[i].e = 0;
  2556 			targetUpdateDelay = maxTargetDelay;
  2557 		}
  2558 	}
  2559 }
  2560 
  2561 void Avatar::updateTargetQuads(float dt)
  2562 {
  2563 
  2564 	particleManager->setSuckPosition(1, dsq->getGameCursorPosition());
  2565 
  2566 	/*
  2567 	for (int i = 0; i < targetQuads.size(); i++)
  2568 	{
  2569 		
  2570 	}
  2571 	*/
  2572 
  2573 	static Entity *lastTargetE = 0;
  2574 	const float tt = 0.02;
  2575 	for (int i = 0; i < targets.size(); i++)
  2576 	{
  2577 		if (targets[i].e)
  2578 		{
  2579 
  2580 			targetQuads[i]->alpha.interpolateTo(1, 0.1);
  2581 			Entity *e = targets[i].e;
  2582 			if (lastTargetE != e)
  2583 			{
  2584 				dsq->sound->playSfx("target-lock");
  2585 				lastTargetE = e;
  2586 			}
  2587 			else
  2588 			{
  2589 				//targetQuads[i]->position.interpolateTo(targets[i].pos, 0.01);
  2590 			}
  2591 			targetQuads[i]->position.interpolateTo(targets[i].pos, tt);
  2592 			targets[i].pos = e->getTargetPoint(targets[i].targetPt);
  2593 			if (i == 0)
  2594 			{
  2595 				particleManager->setSuckPosition(1, targets[i].pos);
  2596 			}
  2597 
  2598 			/*
  2599 			Emitter *em = targetQuads[i];
  2600 			if (!em->isRunning())
  2601 			{
  2602 				em->start();
  2603 			}
  2604 			*/
  2605 		}
  2606 		else
  2607 		{
  2608 			targetQuads[i]->position = dsq->getGameCursorPosition();
  2609 			//targetQuads[i]->alpha.interpolateTo(0, 0.1);
  2610 		}
  2611 	}
  2612 
  2613 	if (targets.empty())
  2614 	{
  2615 		for (int i = 0; i < targetQuads.size(); i++)
  2616 		{
  2617 			if (lastTargetE != 0)
  2618 			{
  2619 				lastTargetE = 0;
  2620 			}
  2621 			//targetQuads[i]->position.interpolateTo(dsq->getGameCursorPosition(),tt);
  2622 			/*
  2623 			std::ostringstream os;
  2624 			os << "setting targetQuads[i] to game cursor, is running = " << targetQuads[i]->isRunning(); 
  2625 			debugLog(os.str());
  2626 			*/
  2627 
  2628 			targetQuads[i]->position = dsq->getGameCursorPosition();
  2629 			if (dsq->continuity.form == FORM_ENERGY && isInputEnabled())
  2630 			{
  2631 				if (dsq->inputMode == INPUT_JOYSTICK && targetQuads[i]->isRunning())
  2632 				{
  2633 					targetQuads[i]->stop();
  2634 				}
  2635 				else if (dsq->inputMode != INPUT_JOYSTICK && !targetQuads[i]->isRunning())
  2636 				{
  2637 					targetQuads[i]->start();
  2638 				}
  2639 			}
  2640 
  2641 			/*
  2642 			if (targetQuads[i]->isRunning())
  2643 			{
  2644 				targetQuads[i]->stop();
  2645 			}
  2646 			*/
  2647 		}
  2648 	}
  2649 }
  2650 
  2651 //fireAtNearestValidEntity("Fire", DT_AVATAR_ENERGYBLAST);
  2652 bool Avatar::fireAtNearestValidEntity(const std::string &shot)
  2653 {
  2654 	if (state.swimTimer > 0)
  2655 		state.swimTimer -= 0.5;
  2656 
  2657 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
  2658 
  2659 	targetUpdateDelay = 0;
  2660 	if (targetUpdateDelay < 0)
  2661 		targetUpdateDelay = 0;
  2662 	//bool big = false;
  2663 	Entity *target = 0;
  2664 
  2665 	Vector dir;
  2666 	Vector p = position;
  2667 	p = boneLeftArm->getWorldPosition();
  2668 	//&& !dsq->game->isObstructed(TileVector(position))
  2669 	/*
  2670 	if (dsq->inputMode == INPUT_MOUSE && state.lockedToWall )
  2671 		dir = dsq->getGameCursorPosition() - p;
  2672 	else
  2673 	*/
  2674 	dir = getAim();
  2675 
  2676 	ShotData *shotData = Shot::getShotData(shot);
  2677 
  2678 
  2679 	bool aimAt = (dir.z == 1.0);
  2680 	//bool aimAt = true;
  2681 	dir.z = 0;
  2682 	Entity *closest = 0;
  2683 	Vector targetPosition;
  2684 
  2685 	int maxTargets = 1;
  2686 
  2687 	//std::vector<Target>targets;
  2688 
  2689 	bool firedShot = false;
  2690 	//int homing = 0;
  2691 	/*
  2692 	if (target)
  2693 	{
  2694 		if (dsq->inputMode != INPUT_JOYSTICK && vel.isLength2DIn(50))
  2695 		{
  2696 		}
  2697 		else
  2698 		{
  2699 
  2700 		}
  2701 		homing = home;
  2702 	}
  2703 	else
  2704 		homing = 0;
  2705 	*/
  2706 
  2707 	/*
  2708 	if (!dir.isLength2DIn(2))
  2709 	{
  2710 	*/
  2711 	Shot *s = 0;
  2712 	bool clearTargets = false;
  2713 
  2714 	// allow autoAim if desired
  2715 	if ((dsq->inputMode == INPUT_JOYSTICK && !aimAt) || dsq->user.control.autoAim)
  2716 	{
  2717 		if (targets.empty())
  2718 		{
  2719 			// force a grab of the nearest targets
  2720 			updateTargets(shotData->damageType, true);
  2721 			// clear the targets after
  2722 			clearTargets = true;
  2723 		}
  2724 	}
  2725 
  2726 	if (!targets.empty())
  2727 	{
  2728 		//homing = home;
  2729 		for (int i = 0; i < targets.size(); i++)
  2730 		{
  2731 			/*
  2732 			if (!aimAt)
  2733 			{
  2734 				dir = targets[i].pos - p;
  2735 			}
  2736 			*/
  2737 				/*
  2738 				std::ostringstream os;
  2739 				os << "shotdir(" << dir.x << ", " << dir.y << ")";
  2740 				debugLog(os.str());
  2741 				*/
  2742 
  2743 
  2744 				/*
  2745 				Vector oldDir = dir;
  2746 
  2747 				dir.normalize2D();
  2748 				dir = (dir + oldDir)/2.0;
  2749 				*/
  2750 
  2751 			if (!aimAt)
  2752 			{
  2753 				dir = (targets[i].e->getTargetPoint(targets[i].targetPt) - p);
  2754 			}
  2755 
  2756 			s = dsq->game->fireShot(shot, this, targets[i].e);
  2757 			s->setAimVector(dir);
  2758 			s->setTargetPoint(targets[i].targetPt);
  2759 
  2760 			/*
  2761 			if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
  2762 			{
  2763 				s = dsq->game->fireShot("EnergyBlast2", this, targets[i].e);
  2764 				s->setAimVector(dir);
  2765 				s->setTargetPoint(targets[i].targetPt);
  2766 			}
  2767 			else
  2768 			{
  2769 				s = dsq->game->fireShot("EnergyBlast", this, targets[i].e);
  2770 				s->setAimVector(dir);
  2771 				s->setTargetPoint(targets[i].targetPt);
  2772 			}
  2773 			*/
  2774 		}
  2775 	}
  2776 	else
  2777 	{
  2778 		//if (!dir.isLength2DIn(2) || dsq->inputMode == INPUT_JOYSTICK)
  2779 		if (true)
  2780 		{
  2781 			s = dsq->game->fireShot(shot, this);
  2782 
  2783 			if (dir.isLength2DIn(2))
  2784 			{
  2785 				if (!vel.isLength2DIn(2))
  2786 					s->setAimVector(vel);
  2787 				else // standing still
  2788 				{
  2789 					Vector dir = getForward();
  2790 					if (isfh())
  2791 					{
  2792 						dir = dir.getPerpendicularRight();
  2793 					}
  2794 					else
  2795 						dir = dir.getPerpendicularLeft();
  2796 					s->setAimVector(dir);
  2797 				}
  2798 			}
  2799 			else
  2800 			{
  2801 				s->setAimVector(dir);
  2802 			}
  2803 			/*
  2804 			if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
  2805 			{
  2806 				s = dsq->game->fireShot("EnergyBlast2", this);
  2807 				s->setAimVector(dir);
  2808 			}
  2809 			else
  2810 			{
  2811 				s = dsq->game->fireShot("EnergyBlast", this);
  2812 				s->setAimVector(dir);
  2813 			}
  2814 			*/
  2815 		}
  2816 	}
  2817 
  2818 	if (s)
  2819 	{
  2820 		checkUpgradeForShot(s);
  2821 
  2822 		
  2823 
  2824 		skeletalSprite.transitionAnimate("fireBlast", 0.1, 0, 5);
  2825 		s->position = p;
  2826 		//s->damageType = dt;
  2827 		/*
  2828 		if (!targets.empty())
  2829 			s->damage = float(damage)/float(targets.size());
  2830 		*/
  2831 		firedShot = true;
  2832 	}
  2833 
  2834 	if (clearTargets)
  2835 	{
  2836 		targets.clear();
  2837 		// try to avoid targets sticking
  2838 		updateTargetQuads(shotData->damageType);
  2839 	}
  2840 
  2841 	return firedShot;
  2842 }
  2843 
  2844 void Avatar::spawnSeed()
  2845 {
  2846 	// max spore children/seeds = 50
  2847 	if (dsq->game->getNumberOfEntitiesNamed("SporeChild") < 4)
  2848 	{
  2849 		if (!dsq->game->isObstructed(TileVector(position)))
  2850 		{
  2851 			Entity *seed = dsq->game->createEntity("SporeChild", 0, position, 0, 0, "");
  2852 		}
  2853 	}
  2854 	else
  2855 	{
  2856 		// visual effect and/or sound effect
  2857 	}
  2858 }
  2859 
  2860 Vector Avatar::getFacing()
  2861 {
  2862 	if (vel.isLength2DIn(2) && rotation.z == 0)
  2863 	{
  2864 		if (isfh())
  2865 			return Vector(1,0);
  2866 		else
  2867 			return Vector(-1,0);
  2868 	}
  2869 	return getForward();
  2870 }
  2871 
  2872 void Avatar::switchDualFormMode()
  2873 {
  2874 	//debugLog("dualForm: changing");
  2875 
  2876 	dsq->sound->playSfx("dualform-switch");
  2877 
  2878 	dsq->overlay->color = Vector(1,1,1);
  2879 	dsq->fade(1, 0);
  2880 	dsq->fade(0, 0.5);
  2881 
  2882 	if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
  2883 		dsq->continuity.dualFormMode = Continuity::DUALFORM_LI;
  2884 	else
  2885 		dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
  2886 
  2887 	refreshDualFormModel();
  2888 }
  2889 
  2890 bool Avatar::hasThingToActivate()
  2891 {
  2892 	return ((pathToActivate != 0) || (entityToActivate != 0));
  2893 }
  2894 
  2895 void Avatar::formAbility(int ability)
  2896 {
  2897 	if (hasThingToActivate()) return;
  2898 	//debugLog("form ability function");
  2899 	switch(dsq->continuity.form)
  2900 	{
  2901 	case FORM_DUAL:
  2902 		{
  2903 			debugLog("dual form ability");
  2904 			/*
  2905 			if (this->getVectorToCursorFromScreenCentre().isLength2DIn(minMouse))
  2906 			{
  2907 				debugLog("in and changing");
  2908 				if (dualFormMode == DUALFORM_NAIJA)
  2909 					dualFormMode = DUALFORM_LI;
  2910 				else
  2911 					dualFormMode = DUALFORM_NAIJA;
  2912 				refreshDualFormModel();
  2913 			}
  2914 			else
  2915 			*/
  2916 			{
  2917 				/*
  2918 				if (chargeLevelAttained == 2)
  2919 				{
  2920 					if (dualFormMode == DUALFORM_NAIJA)
  2921 						dualFormMode = DUALFORM_LI;
  2922 					else
  2923 						dualFormMode = DUALFORM_NAIJA;
  2924 					refreshDualFormModel();
  2925 				}
  2926 				else
  2927 				*/
  2928 				{
  2929 					if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
  2930 					{
  2931 						
  2932 						// ~~~~~~~~~~ SOUL SCREAM
  2933 						
  2934 						if (chargeLevelAttained == 1)
  2935 						{
  2936 							if (dsq->continuity.dualFormCharge >= requiredDualFormCharge)
  2937 							{
  2938 								core->sound->playSfx("DualForm-Scream");
  2939 
  2940 								if (core->afterEffectManager)
  2941 									core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
  2942 
  2943 								dsq->continuity.dualFormCharge = 0;
  2944 								dsq->shakeCamera(25, 2);
  2945 
  2946 								core->globalScale = Vector(0.4, 0.4);
  2947 								myZoom = Vector(0.4, 0.4);
  2948 
  2949 								/*
  2950 								setv(EV_NOINPUTNOVEL, 0);
  2951 								core->globalScale = Vector(1.5, 1.5);
  2952 								core->main(0.5);
  2953 								setv(EV_NOINPUTNOVEL, 1);
  2954 								*/
  2955 
  2956 
  2957 								FOR_ENTITIES(i)
  2958 								{
  2959 									Entity *e = *i;
  2960 									if (e->getEntityType() == ET_ENEMY && e != this)
  2961 									{
  2962 										if (e->isv(EV_SOULSCREAMRADIUS, -1) || (e->position - position).isLength2DIn(1000 + e->getv(EV_SOULSCREAMRADIUS)))
  2963 										{
  2964 											DamageData d;
  2965 											d.damage = 20;
  2966 											d.damageType = DT_AVATAR_DUALFORMNAIJA;
  2967 											d.attacker = this;
  2968 											d.form = dsq->continuity.form;
  2969 											e->damage(d);
  2970 										}
  2971 									}
  2972 								}
  2973 
  2974 								/*
  2975 								setv(EV_NOINPUTNOVEL, 0);
  2976 								core->main(0.5);
  2977 								dsq->screenTransition->capture();
  2978 								dsq->screenTransition->go(0.5);
  2979 								myZoom = Vector(1,1);
  2980 								setv(EV_NOINPUTNOVEL, 1);
  2981 								*/
  2982 							}
  2983 							else
  2984 							{
  2985 								core->sound->playSfx("Denied");
  2986 							}
  2987 						}
  2988 					}
  2989 					else if (dsq->continuity.dualFormMode == Continuity::DUALFORM_LI)
  2990 					{
  2991 						if (chargeLevelAttained == 1)
  2992 						{
  2993 							int i = 0;
  2994 							int num = 5;
  2995 							for (; i < num; i++)
  2996 							{
  2997 								Shot *s = dsq->game->fireShot("DualForm", this, 0, position, 0);
  2998 								//*0.5 + getAim()*0.5
  2999 								Vector v1 = this->getTendrilAimVector(i, num);
  3000 								Vector v2 = getAim();
  3001 								v1.normalize2D();
  3002 								v2.normalize2D();
  3003 								s->setAimVector(v1*0.1 + v2*0.9);
  3004 							}
  3005 							core->sound->playSfx("DualForm-Shot");
  3006 							dsq->spawnParticleEffect("DualFormFire", position);
  3007 
  3008 							/*
  3009 							didShockDamage = false;
  3010 							doShock("DualFormLiTendril");
  3011 							*/
  3012 						}
  3013 						else
  3014 						{
  3015 							core->sound->playSfx("Denied");
  3016 							/*
  3017 							if (!fireDelay)
  3018 							{
  3019 								if (fireAtNearestValidEntity("DualFormLi"))
  3020 								{
  3021 									fireDelay = fireDelayTime;
  3022 								}
  3023 							}
  3024 							*/
  3025 						}
  3026 					}
  3027 				}
  3028 			}
  3029 			/*
  3030 			else if (ability == 1)
  3031 			{
  3032 
  3033 			}
  3034 			*/
  3035 		}
  3036 	break;
  3037 	case FORM_ENERGY:
  3038 		{
  3039 			if (ability == 0)
  3040 			{
  3041 				if (chargeLevelAttained == 2)
  3042 				{
  3043 					/*
  3044 					shockTimer = 1;
  3045 					damageDelay = 0.2;
  3046 					didShockDamage = false;
  3047 					*/
  3048 					didShockDamage = false;
  3049 					if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
  3050 						doShock("EnergyTendril2");
  3051 					else
  3052 						doShock("EnergyTendril");
  3053 					if (!state.lockedToWall)
  3054 						skeletalSprite.animate("energyChargeAttack", 0, 6);
  3055 
  3056 					/*
  3057 					if (core->afterEffectManager)
  3058 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter, 0.1,0.03,30,0.2f, 1.5));
  3059 					*/
  3060 					dsq->playVisualEffect(VFX_SHOCK, position, this);
  3061 				}
  3062 				else
  3063 				{
  3064 					if (!fireDelay)
  3065 					{
  3066 						std::string shotName;
  3067 						if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
  3068 							shotName = "EnergyBlast2";
  3069 						else
  3070 							shotName = "EnergyBlast";
  3071 
  3072 						if (fireAtNearestValidEntity(shotName))
  3073 						{
  3074 							fireDelay = fireDelayTime;
  3075 						}
  3076 					}
  3077 				}
  3078 			}
  3079 		}
  3080 		break;
  3081 	case FORM_NATURE:
  3082 		// no abilities
  3083 		{
  3084 			//debugLog("rock ability");
  3085 			if (ability == 0)
  3086 			{
  3087 				if (formAbilityDelay == 0)
  3088 				{
  3089 					formAbilityDelay = 0.2;
  3090 					//Vector pos = dsq->getGameCursorPosition() - position;
  3091 
  3092 					Vector pos = getAim();
  3093 					if (!pos.isZero())
  3094 						pos.setLength2D(16);
  3095 					pos += position;
  3096 
  3097 					std::string seedName;
  3098 					if (chargeLevelAttained == 0)
  3099 						seedName = "SeedFlower";
  3100 					else if (chargeLevelAttained == 2)
  3101 						seedName = "SeedUberVine";
  3102 
  3103 					Shot *s = dsq->game->fireShot(seedName, this, 0, pos, getAim());
  3104 
  3105 
  3106 					/*
  3107 					Vector pos = getAim();
  3108 					if (!pos.isZero())
  3109 						pos.setLength2D(64);
  3110 					pos += position;
  3111 
  3112 
  3113 					//dsq->spawnParticleEffect("Fertilizer", pos);
  3114 
  3115 					Entity *e = 0;
  3116 					std::string seedName;
  3117 					if (chargeLevelAttained == 0)
  3118 						seedName = "SeedFlower";
  3119 					else if (chargeLevelAttained == 2)
  3120 						seedName = "SeedUberVine";
  3121 
  3122 					e = dsq->game->createEntity(seedName, 0, pos, 0, false, "");
  3123 
  3124 					Vector add = pos - position;
  3125 					add.setLength2D(800);
  3126 					e->vel += add;
  3127 					*/
  3128 
  3129 					/*
  3130 					if (chargeLevelAttained == 0)
  3131 					{
  3132 					}
  3133 					else if (chargeLevelAttained == 1)
  3134 					{
  3135 						e->setState(STATE_CHARGE1);
  3136 					}
  3137 					else if (chargeLevelAttained == 2)
  3138 					{
  3139 						e->setState(STATE_CHARGE2);
  3140 					}
  3141 
  3142 					e->update(0);
  3143 					*/
  3144 					// idle = charge 0
  3145 					// attack = charge1
  3146 					// something = charge2
  3147 					/*
  3148 					for (int i = 0; i < dsq->entities.size(); i++)
  3149 					{
  3150 						Entity *e = dsq->entities[i];
  3151 						if (e && e->getEntityType() == ET_ENEMY && e->isDamageTarget(DT_AVATAR_NATURE))
  3152 						{
  3153 							if ((e->position - pos).isLength2DIn(128))
  3154 							{
  3155 								DamageData d;
  3156 								d.damageType = DT_AVATAR_NATURE;
  3157 								d.damage = 1;
  3158 								d.attacker = this;
  3159 								e->damage(d);
  3160 							}
  3161 						}
  3162 					}
  3163 					*/
  3164 				}
  3165 			}
  3166 		}
  3167 	break;
  3168 	case FORM_BEAST:
  3169 		{
  3170 
  3171 			if (!dsq->continuity.isNaijaEatsEmpty())
  3172 			{
  3173 				EatData *d = dsq->continuity.getLastNaijaEat();
  3174 				if (!d->shot.empty())
  3175 				{
  3176 					int num = getNumShots()-2;
  3177 
  3178 					for (int i = 0; i < num; i++)
  3179 					{
  3180 						bool playSfx = true;
  3181 						if (i > 0)
  3182 							playSfx = false;
  3183 						Shot *s = dsq->game->fireShot(d->shot, this, 0, Vector(0,0,0), Vector(0,0,0), playSfx);
  3184 						if (s->shotData && s->shotData->damage > 0)
  3185 						{
  3186 							s->extraDamage = 1;
  3187 						}
  3188 
  3189 						Entity *target = 0;
  3190 						if (s->shotData->homing > 0)
  3191 						{
  3192 							int sml = -1;
  3193 							Vector p = dsq->getGameCursorPosition();
  3194 							target = dsq->game->getNearestEntity(p, 800, this, ET_ENEMY, s->shotData->damageType);
  3195 						}
  3196 						if (target)
  3197 						{
  3198 							s->target = target;
  3199 						}
  3200 
  3201 						if (bone_head)
  3202 						{
  3203 							s->position = bone_head->getWorldPosition();
  3204 						}
  3205 						else
  3206 						{
  3207 							s->position = this->position;
  3208 						}
  3209 
  3210 						if (num == 1)
  3211 							s->setAimVector(this->getVectorToCursor());
  3212 						else
  3213 						{
  3214 							Vector aim = this->getVectorToCursor();
  3215 							aim.normalize2D();
  3216 							s->setAimVector(getTendrilAimVector(i, num)*0.1 + aim*0.9);
  3217 						}
  3218 
  3219 						if (s->shotData && s->shotData->avatarKickBack)
  3220 						{
  3221 							Vector d = s->velocity;
  3222 							d.setLength2D(-s->shotData->avatarKickBack);
  3223 							float effect = 1;
  3224 							if (!isUnderWater())
  3225 								effect = 0.4;
  3226 							push(d, s->shotData->avatarKickBackTime * effect, s->shotData->avatarKickBack * effect, 0);
  3227 						}
  3228 					}
  3229 
  3230 					debugLog("firing: " + d->shot);
  3231 					d->ammo--;
  3232 				}
  3233 				if (d->ammo <= 0)
  3234 					dsq->continuity.removeLastNaijaEat();
  3235 					//dsq->continuity.removeEatData(eats.size()-1);
  3236 			}
  3237 
  3238 			/*
  3239 			switch(inTummy)
  3240 			{
  3241 			case EAT_BASICSHOT:
  3242 			{
  3243 				tummyAmount --;
  3244 				// FIRE!
  3245 				if (tummyAmount <= 0)
  3246 				{
  3247 
  3248 					//fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000);
  3249 					inTummy = EAT_NONE;
  3250 				}
  3251 			}
  3252 			break;
  3253 			}
  3254 			*/
  3255 			/*
  3256 			if (inTummy > 0)
  3257 			{
  3258 				if (inTummy > 3)
  3259 					inTummy = 3;
  3260 				if (fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000))
  3261 				{
  3262 					inTummy = 0;
  3263 				}
  3264 			}
  3265 			*/
  3266 			/*
  3267 			if (ability == 0)
  3268 			{
  3269 				Vector bitePos = position;
  3270 				Vector offset = vel;
  3271 				offset.setLength2D(128);
  3272 				bitePos += offset;
  3273 
  3274 				for (int i = 0; i < dsq->entities.size(); i++)
  3275 				{
  3276 					Entity *e = dsq->entities[i];
  3277 
  3278 					if (e && (e->position - bitePos).getSquaredLength2D() < sqr(64))
  3279 					{
  3280 						DamageData d;
  3281 						d.attacker = this;
  3282 						d.damage = 2;
  3283 						e->damage(d);
  3284 						heal(1);
  3285 					}
  3286 				}
  3287 			}
  3288 			else
  3289 			{
  3290 			}
  3291 			*/
  3292 		}
  3293 	break;
  3294 	case FORM_SUN:
  3295 	{
  3296 		if (formAbilityDelay == 0 && chargeLevelAttained==1)
  3297 		{
  3298 			core->sound->playSfx("SunForm");
  3299 			//dsq->spawnParticleEffect("LightFlare", position);
  3300 
  3301 			chargeEmitter->load("SunFlare");
  3302 			chargeEmitter->start();
  3303 
  3304 			PauseQuad *q = new PauseQuad;
  3305 			q->setTexture("Naija/LightFormGlow");
  3306 			q->position = position;
  3307 			q->setWidthHeight(1024, 1024);
  3308 			q->setLife(1);
  3309 			q->setDecayRate(0.05);
  3310 			q->fadeAlphaWithLife = 1;
  3311 			q->scale = Vector(0,0);
  3312 			q->scale.interpolateTo(Vector(2,2), 0.1);
  3313 			dsq->game->addRenderObject(q, LR_ELEMENTS13);
  3314 
  3315 			FOR_ENTITIES(i)
  3316 			{
  3317 				Entity *e = *i;
  3318 				if (e != this && (e->position - position).isLength2DIn(2048))
  3319 				{
  3320 					e->lightFlare();
  3321 				}
  3322 			}
  3323 			//formAbilityDelay = 0.1;
  3324 		}
  3325 	}
  3326 	break;
  3327 	case FORM_SPIRIT:
  3328 		// spirit beacon
  3329 		// absorbs nearby shots, and respawns the player if in a "SPIRITBEACON" node
  3330 		if (formAbilityDelay == 0)
  3331 		{
  3332 			core->sound->playSfx("Spirit-Beacon");
  3333 			//dsq->spawnParticleEffect("SpiritBeacon", position);
  3334 			std::list<Shot*> delShots;
  3335 			Shot::Shots::iterator i;
  3336 			for (i = Shot::shots.begin(); i != Shot::shots.end(); i++)
  3337 			{
  3338 				Shot *s = (*i);
  3339 				if (s->shotData && s->firer)
  3340 				{
  3341 					if (!s->shotData->invisible && s->firer->getEntityType()==ET_ENEMY)
  3342 					{
  3343 						if ((s->position - position).isLength2DIn(256))
  3344 						{
  3345 							//s->safeKill();
  3346 							delShots.push_back(s);
  3347 							spiritEnergyAbsorbed++;
  3348 						}
  3349 					}
  3350 				}
  3351 			}
  3352 			for (std::list<Shot*>::iterator j = delShots.begin(); j != delShots.end(); j++)
  3353 			{
  3354 				Shot *s = (*j);
  3355 				s->safeKill();
  3356 			}
  3357 			if (spiritEnergyAbsorbed > 4)
  3358 			{
  3359 				dsq->game->spawnManaBall(position, 1);
  3360 				spiritEnergyAbsorbed = 0;
  3361 			}
  3362 			spiritBeaconEmitter.start();
  3363 			formAbilityDelay = 1.0;
  3364 
  3365 			Path *p = dsq->game->getNearestPath(position, "SPIRITBEACON");
  3366 			if (p && p->isCoordinateInside(position))
  3367 			{
  3368 				bodyPosition = position;
  3369 				if (pullTarget)
  3370 				{
  3371 					pullTarget->position = position;
  3372 				}
  3373 				revert();
  3374 			}
  3375 			else
  3376 			{
  3377 				Path *p = dsq->game->getNearestPath(position, PATH_SPIRITPORTAL);
  3378 				if (p && p->isCoordinateInside(position))
  3379 				{
  3380 					if (inSpiritWorld)
  3381 						changeForm(FORM_NORMAL);
  3382 					dsq->game->warpToSceneFromNode(p);
  3383 				}
  3384 			}
  3385 		}
  3386 	break;
  3387 	case FORM_FISH:
  3388 	{
  3389 
  3390 	}
  3391 	break;
  3392 	}
  3393 }
  3394 
  3395 Vector Avatar::getTendrilAimVector(int i, int max)
  3396 {
  3397 	float a = float(float(i)/float(max))*3.14*2;
  3398 	Vector aim(sinf(a), cosf(a));
  3399 	if (state.lockedToWall)
  3400 	{
  3401 		Vector n = dsq->game->getWallNormal(position);
  3402 		if (!n.isZero())
  3403 		{
  3404 			aim = aim*0.4 + n*0.6;
  3405 		}
  3406 	}
  3407 	return aim;
  3408 }
  3409 
  3410 int Avatar::getNumShots()
  3411 {
  3412 	int thits = normalTendrilHits;
  3413 	if (flourishPowerTimer.isActive())
  3414 	{
  3415 		if (lastBurstType == BURST_WALL)
  3416 			thits = maxTendrilHits;
  3417 		else
  3418 			thits = rollTendrilHits;
  3419 	}
  3420 	else
  3421 	{
  3422 		if (bursting)
  3423 		{
  3424 			if (lastBurstType == BURST_WALL)
  3425 				thits = rollTendrilHits;
  3426 		}
  3427 	}
  3428 	return thits;
  3429 }
  3430 
  3431 void Avatar::doShock(const std::string &shotName)
  3432 {
  3433 
  3434 	
  3435 
  3436 	int c = 0;
  3437 	//int maxHit = 2 + dsq->continuity.getSpellLevel(SPELL_SHOCK)*2;
  3438 	//int maxHit = 4;
  3439 	std::vector <Entity*> entitiesToHit;
  3440 	std::vector <Target> localTargets;
  3441 	bool clearTargets = true;
  3442 
  3443 	int thits = getNumShots();
  3444 
  3445 	/*
  3446 	if (skeletalSprite.getAnimationLayer(LAYER_FLOURISH)->getCurrentAnimation())
  3447 	{
  3448 		thits = maxTendrilHits;
  3449 	}
  3450 	*/
  3451 
  3452 	if (!targets.empty() && targets[0].e != 0)
  3453 	{
  3454 		clearTargets = false;
  3455 		for (int i = 0; i < thits; i++)
  3456 		{
  3457 			entitiesToHit.push_back(targets[0].e);
  3458 		}
  3459 	}
  3460 	else
  3461 	{
  3462 		//std::vector <Target> localTargets;
  3463 
  3464 		localTargets.clear();
  3465 
  3466 		int range = 800;
  3467 		EntityList entityList;
  3468 		FOR_ENTITIES(i)
  3469 		{
  3470 			Entity *e = *i;
  3471 			if (e != this && e->isPresent() && e->isDamageTarget(DT_AVATAR_SHOCK) && (e->position - position).isLength2DIn(range))
  3472 			{
  3473 				entityList.push_back(e);
  3474 			}
  3475 		}
  3476 
  3477 		while (c < thits)
  3478 		{
  3479 			Target t = getNearestTarget(position, position, this, DT_AVATAR_SHOCK, true, &localTargets, &entityList);
  3480 			if (t.e)
  3481 			{
  3482 				localTargets.push_back(t);
  3483 				entitiesToHit.push_back(t.e);
  3484 				targets.push_back(t);
  3485 				c ++;
  3486 			}
  3487 			else
  3488 			{
  3489 				break;
  3490 			}
  3491 		}
  3492 
  3493 		if (!localTargets.empty())
  3494 		{
  3495 			while (entitiesToHit.size()<thits)
  3496 			{
  3497 				for (int i = 0; i < localTargets.size(); i++)
  3498 				{
  3499 					if (!(entitiesToHit.size()<thits))
  3500 						break;
  3501 					entitiesToHit.push_back(localTargets[i].e);
  3502 					targets.push_back(localTargets[i]);
  3503 				}
  3504 			}
  3505 		}
  3506 		localTargets.clear();
  3507 	}
  3508 
  3509 	Vector aim = getAim();
  3510 	aim.normalize2D();
  3511 	int sz = entitiesToHit.size();
  3512 	float spread = 3.14;
  3513 
  3514 
  3515 
  3516 
  3517 	if (sz == 0)
  3518 	{
  3519 		for (int i = 0; i < thits; i++)
  3520 		{
  3521 			Shot *s = dsq->game->fireShot(shotName, this, 0);
  3522 
  3523 			s->setAimVector(getTendrilAimVector(i, thits));
  3524 
  3525 			checkUpgradeForShot(s);
  3526 		}
  3527 	}
  3528 	else
  3529 	{
  3530 		for (int i = 0; i < sz; i++)
  3531 		{
  3532 			Entity *e = entitiesToHit[i];
  3533 			if (e)
  3534 			{
  3535 				Shot *s = dsq->game->fireShot(shotName, this, e);
  3536 				if (!targets.empty())
  3537 				{
  3538 					for (int j = 0; j < targets.size(); j++)
  3539 					{
  3540 						if (targets[j].e == e)
  3541 							s->targetPt = targets[j].targetPt;
  3542 					}
  3543 				}
  3544 				/*
  3545 				else if (!localTargets.empty())
  3546 				{
  3547 					for (int j = 0; j < localTargets.size(); j++)
  3548 					{
  3549 						if (localTargets[j].e == e)
  3550 							s->targetPt = localTargets[j].targetPt;
  3551 					}
  3552 				}
  3553 				*/
  3554 				Vector d = e->position - position;
  3555 				/*
  3556 				float a = float(float(i)/float(sz))*3.14*2;
  3557 				Vector aim(sinf(a), cosf(a));
  3558 
  3559 				swizzleTendrilAimVector(aim);
  3560 				*/
  3561 				s->setAimVector(getTendrilAimVector(i, thits));
  3562 				checkUpgradeForShot(s);
  3563 				/*
  3564 				float ang = 0;
  3565 				MathFunctions::calculateAngleBetweenVectorsInRadians(Vector(0,-1), d, ang);
  3566 				float a = i-(sz/2);
  3567 				Vector adjust = a*spread + ang;
  3568 				d.normalize2D();
  3569 				s->setAimVector((d + adjust)/2);
  3570 				*/
  3571 			}
  3572 		}
  3573 	}
  3574 
  3575 	if (clearTargets)
  3576 	{
  3577 		targets.clear();
  3578 	}
  3579 
  3580 
  3581 	/*
  3582 	// old method
  3583 	for (int i = 0; i < entitiesToHit.size(); i++)
  3584 	{
  3585 		Entity *e = entitiesToHit[i];
  3586 		DamageData d;
  3587 		d.attacker = this;
  3588 		d.damageType = DT_AVATAR_SHOCK;
  3589 		d.damage = 3;
  3590 		e->damage(d);
  3591 
  3592 		dsq->playVisualEffect(VFX_SHOCKHIT, e->position, e);
  3593 
  3594 		EnergyTendril *t = new EnergyTendril(this, e);
  3595 		core->addRenderObject(t, LR_PARTICLES);
  3596 
  3597 		e->shock();
  3598 	}
  3599 	*/
  3600 
  3601 	//HACK: WHAT DOES THIS VARIABLE DO EXACTLY?
  3602 	didShockDamage = true;
  3603 
  3604 
  3605 	//loseTargets();
  3606 }
  3607 
  3608 void Avatar::updateShock(float dt)
  3609 {
  3610 	/*
  3611 	if (shockTimer > 0)
  3612 	{
  3613 		float shockTime = 0.75;
  3614 		castShockTimer += dt;
  3615 		std::vector<Entity*> closestEntities;
  3616 		unsigned int c=0;
  3617 		const float shotDelayTime = 0.05;
  3618 
  3619 
  3620 		if (damageDelay > 0)
  3621 		{
  3622 			damageDelay -= dt;
  3623 			if (damageDelay <= 0)
  3624 				damageDelay = 0;
  3625 		}
  3626 
  3627 		if (damageDelay == 0)
  3628 		{
  3629 		}
  3630 
  3631 		if (damageDelay == 0)
  3632 		{
  3633 			damageDelay = 999;
  3634 		}
  3635 
  3636 		shockTimer -= dt;
  3637 		if (shockTimer < 0)
  3638 		{
  3639 			shockTimer = 0;
  3640 		}
  3641 	}
  3642 	*/
  3643 }
  3644 
  3645 void Avatar::formAbilityUpdate(float dt)
  3646 {
  3647 	switch(dsq->continuity.form)
  3648 	{
  3649 	case FORM_FISH:
  3650 	{
  3651 		if (core->mouse.buttons.right)
  3652 		{
  3653 			const float bubbleRate = 0.2;
  3654 
  3655 			state.abilityDelay -= dt;
  3656 			if (state.abilityDelay < 0)
  3657 				state.abilityDelay = 0;
  3658 
  3659 			if (state.abilityDelay == 0)
  3660 			{
  3661 				state.abilityDelay = bubbleRate;
  3662 				//state.abilityDelay -= bubbleRate;
  3663 				// spawn bubble
  3664 				//Entity *bubble = dsq->game->createEntity("FishFormBubble", 0, position, 0, false, "");
  3665 				Vector dir = getAim();
  3666 				dir.normalize2D();
  3667 
  3668 				dsq->game->fireShot("FishFormBubble", this, 0, position+dir*16, dir);
  3669 			}
  3670 		}
  3671 	}
  3672 	break;
  3673 	case FORM_ENERGY:
  3674 	{
  3675 		/*
  3676 		if (core->mouse.buttons.right && mana > 0 && !core->mouse.buttons.left)
  3677 		{
  3678 			float shockTime = 0.75;
  3679 			castShockTimer += dt;
  3680 			std::vector<Entity*> closestEntities;
  3681 			unsigned int c=0;
  3682 			const float shotDelayTime = 0.05;
  3683 
  3684 
  3685 			int maxHit = 2 + dsq->continuity.getSpellLevel(SPELL_SHOCK)*2;
  3686 			for (int i = 0; i < dsq->entities.size(); i++)
  3687 			{
  3688 				Entity *e = dsq->entities[i];
  3689 				Vector d = e->position - this->position;
  3690 				if (e != this && !e->isEntityDead() && e->isAffectedBySpell(SPELL_SHOCK) && d.getSquaredLength2D() < sqr(400+(dsq->continuity.getSpellLevel(SPELL_SHOCK)-1)*100))
  3691 				{
  3692 					state.shotDelay += dt;
  3693 					if (state.shotDelay > shotDelayTime)
  3694 					{
  3695 						state.shotDelay -= shotDelayTime;
  3696 
  3697 						EnergyTendril *t = new EnergyTendril(avatar->position, e->position);
  3698 						core->addRenderObject(t, LR_PARTICLES);
  3699 					}
  3700 					e->offset.x = rand()%5;
  3701 					e->shock();
  3702 					DamageData d;
  3703 					d.attacker = this;
  3704 					d.spellType = SPELL_SHOCK;
  3705 					d.damage = 1+(dsq->continuity.getSpellLevel(SPELL_SHOCK)-1)*1;
  3706 					e->hit(d);
  3707 					c ++;
  3708 				}
  3709 				if (c >= maxHit)
  3710 					break;
  3711 			}
  3712 			//std::ostringstream os;
  3713 			//os << "castShockTimer: " << castShockTimer << " - shockTime: " << shockTime;
  3714 			//debugLog(os.str());
  3715 			if (castShockTimer > shockTime)
  3716 			{
  3717 				castShockTimer -= shockTime;
  3718 				mana --;
  3719 			}
  3720 		}
  3721 		else
  3722 		{
  3723 			state.shotDelay = 0;
  3724 			endShock();
  3725 		}
  3726 		*/
  3727 	}
  3728 	break;
  3729 	}
  3730 }
  3731 
  3732 bool Avatar::isMouseInputEnabled()
  3733 {
  3734 	if (!inputEnabled) return false;
  3735 	//if (dsq->continuity.getWorldType() != WT_NORMAL) return false;
  3736 	//if (getState() != STATE_IDLE) return false;
  3737 	if (dsq->game->isPaused()) return false;
  3738 	return true;
  3739 }
  3740 
  3741 int rmb_flag = 0;
  3742 void Avatar::rmbd2()
  3743 {
  3744 	rmb_flag = 1;
  3745 	rmbd();
  3746 	rmb_flag = 0;
  3747 }
  3748 
  3749 void Avatar::rmbd()
  3750 {
  3751 	//core->setDockIcon("BitBlot");
  3752 	if (!isMouseInputEnabled() || isEntityDead()) return;
  3753 	if (dsq->continuity.form == FORM_NORMAL )
  3754 	{
  3755 		//if (isCoordinateInRadius(dsq->getGameCursorPosition(), 96))
  3756 		///Vector diff = core->mouse.position - c;
  3757 		if (dsq->inputMode == INPUT_MOUSE && !rmb_flag)
  3758 		{
  3759 			Vector diff = getVectorToCursorFromScreenCentre();
  3760 			if (diff.getSquaredLength2D() < sqr(openSingingInterfaceRadius))
  3761 				openSingingInterface();
  3762 		}
  3763 		else
  3764 		{
  3765 			openSingingInterface();
  3766 		}
  3767 	}
  3768 	else
  3769 	{
  3770 		if (spellCastDelay == 0)
  3771 			startCharge(0);
  3772 	}
  3773 	/*
  3774 	if (spellCastDelay == 0)
  3775 		startCharging(1);
  3776 	*/
  3777 }
  3778 
  3779 void Avatar::rmbu()
  3780 {
  3781 	if (!isMouseInputEnabled() || isEntityDead()) return;
  3782 
  3783 	if (charging)
  3784 	{
  3785 		if (!entityToActivate && !pathToActivate)
  3786 			formAbility(0);
  3787 
  3788 		endCharge();
  3789 	}
  3790 
  3791 
  3792 	dsq->cursorGlow->alpha.interpolateTo(0, 0.2);
  3793 	dsq->cursorBlinker->alpha.interpolateTo(0, 0.2);
  3794 
  3795 	if (pickingPullTarget)
  3796 	{
  3797 		if (potentialPullTarget)
  3798 		{
  3799 			pullTarget = potentialPullTarget;
  3800 			debugLog("Calling start pull");
  3801 			pullTarget->startPull();
  3802 		}
  3803 		closePullTargetInterface();
  3804 	}
  3805 
  3806 	if (singing)
  3807 	{
  3808 		closeSingingInterface();
  3809 	}
  3810 
  3811 
  3812 	if (entityToActivate)
  3813 	{
  3814 		activateEntity = entityToActivate;
  3815 		entityToActivate = 0;
  3816 	}
  3817 	if (pathToActivate)
  3818 	{
  3819 		pathToActivate->activate();
  3820 		pathToActivate = 0;
  3821 	}
  3822 
  3823 
  3824 
  3825 	/*
  3826 	if (charging)
  3827 	{
  3828 		formAbility(1);
  3829 		endCharge();
  3830 	}
  3831 	*/
  3832 }
  3833 
  3834 bool Avatar::canCharge(int ability)
  3835 {
  3836 	switch(dsq->continuity.form)
  3837 	{
  3838 	case FORM_ENERGY:
  3839 		if (ability == 0) return true;
  3840 	break;
  3841 	case FORM_BEAST:
  3842 		//if (inTummy) return true;
  3843 	break;
  3844 	case FORM_DUAL:
  3845 		/*
  3846 		if (dualFormMode == DUALFORM_NAIJA)
  3847 		{
  3848 			if (dualFormCharge >= requiredDualFormCharge)
  3849 			{
  3850 				return true;
  3851 			}
  3852 		}
  3853 		else
  3854 		{
  3855 			return true;
  3856 		}
  3857 		*/
  3858 		return true;
  3859 	break;
  3860 	case FORM_NATURE:
  3861 		if (ability == 0)
  3862 			return true;
  3863 	break;
  3864 	case FORM_SUN:
  3865 		return true;
  3866 	break;
  3867 	}
  3868 	return false;
  3869 }
  3870 
  3871 void Avatar::startCharge(int ability)
  3872 {
  3873 	if (!isCharging() && canCharge(ability))
  3874 	{
  3875 		if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
  3876 		{
  3877 			core->sound->stopSfx(dsq->loops.charge);
  3878 			dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
  3879 		}
  3880 
  3881 		PlaySfx sfx;
  3882 		sfx.name = "ChargeLoop";
  3883 		sfx.loops = -1;
  3884 		dsq->loops.charge = core->sound->playSfx(sfx);
  3885 
  3886 		state.spellCharge = 0;
  3887 		spellChargeMin = 0;
  3888 		chargeLevelAttained = 0;
  3889 
  3890 		/*
  3891 		chargeGraphic->alpha = 0;
  3892 		chargeGraphic->scale = Vector(0,0);
  3893 		chargeGraphic->alpha.interpolateTo(0.6, chargeMax, 0);
  3894 		float sz = 1.5;
  3895 		chargeGraphic->scale.interpolateTo(Vector(sz,sz), chargeMax, 0);
  3896 		*/
  3897 
  3898 		switch(dsq->continuity.form)
  3899 		{
  3900 		case FORM_ENERGY:
  3901 			chargingEmitter->load("ChargingEnergy");
  3902 		break;
  3903 		case FORM_NATURE:
  3904 			chargingEmitter->load("ChargingNature");
  3905 		break;
  3906 		case FORM_SUN:
  3907 			chargingEmitter->load("ChargingEnergy");
  3908 		break;
  3909 		case FORM_DUAL:
  3910 			chargingEmitter->load("ChargingDualForm");
  3911 		break;
  3912 		default:
  3913 			chargingEmitter->load("ChargingGeneric");
  3914 		break;
  3915 		}
  3916 
  3917 		chargingEmitter->start();
  3918 
  3919 		charging = true;
  3920 		abilityCharging = ability;
  3921 
  3922 	}
  3923 	if (!canCharge(ability))
  3924 	{
  3925 		formAbility(ability);
  3926 	}
  3927 }
  3928 
  3929 void Avatar::setBlockSinging(bool v)
  3930 {
  3931 	blockSinging = v;
  3932 }
  3933 
  3934 bool Avatar::canSetBoneLock()
  3935 {
  3936 	/*
  3937 	if (dsq->continuity.form == FORM_FISH || dsq->continuity.form == FORM_SPIRIT)
  3938 		return false;
  3939 	*/
  3940 
  3941 	return true;
  3942 }
  3943 
  3944 void Avatar::onSetBoneLock()
  3945 {
  3946 	Entity::onSetBoneLock();
  3947 
  3948 	if (boneLock.on)
  3949 	{
  3950 		skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
  3951 		lockToWallCommon();
  3952 		state.lockedToWall = 1;
  3953 		wallNormal = boneLock.localOffset;
  3954 		wallNormal.normalize2D();
  3955 		rotateToVec(wallNormal, 0.1);
  3956 	}
  3957 	else
  3958 	{
  3959 		if (state.lockedToWall)
  3960 		{
  3961 			fallOffWall();
  3962 		}
  3963 	}
  3964 }
  3965 
  3966 void Avatar::onUpdateBoneLock()
  3967 {
  3968 	Entity::onUpdateBoneLock();
  3969 
  3970 	wallNormal = boneLock.wallNormal;
  3971 	rotateToVec(wallNormal, 0.01);
  3972 }
  3973 
  3974 void Avatar::lmbd()
  3975 {
  3976 	if (!isMouseInputEnabled()) return;
  3977 
  3978 	// getstopdistance
  3979 	if (_isUnderWater)
  3980 	{
  3981 		Vector v = getVectorToCursor();
  3982 		if (v.isLength2DIn(getStopDistance()) && !v.isLength2DIn(minMouse))
  3983 		{
  3984 			if (state.lockedToWall)
  3985 			{
  3986 				fallOffWall();
  3987 			}
  3988 		}
  3989 	}
  3990 	/*
  3991 	else
  3992 	{
  3993 		if (spellCastDelay == 0)
  3994 			startCharging(0);
  3995 	}
  3996 	*/
  3997 }
  3998 
  3999 void Avatar::fallOffWall()
  4000 {
  4001 	//stillTimer.stop();
  4002 	if (state.lockedToWall)
  4003 	{
  4004 		lockToWallFallTimer = 0;
  4005 		state.nearWall = false;
  4006 		state.lockedToWall = false;
  4007 
  4008 		setBoneLock(BoneLock());
  4009 
  4010 
  4011 		idle();
  4012 		offset.interpolateTo(Vector(0,0), 0.1);
  4013 		if (!wallNormal.isZero())
  4014 		{
  4015 			Vector velSet = wallNormal;
  4016 			velSet.setLength2D(200);
  4017 			vel += velSet;
  4018 		}
  4019 		//doCollisionAvoidance(dt, 5, 1);
  4020 	}
  4021 }
  4022 
  4023 void Avatar::lmbu()
  4024 {
  4025 	if (!isMouseInputEnabled()) return;
  4026 
  4027 	if (dsq->continuity.toggleMoveMode)
  4028 		movingOn = !movingOn;
  4029 
  4030 	if (isSinging())
  4031 	{
  4032 		// switch menu
  4033 	}
  4034 }
  4035 
  4036 bool Avatar::isCharging()
  4037 {
  4038 	return charging;
  4039 }
  4040 
  4041 void Avatar::endCharge()
  4042 {
  4043 	if (charging)
  4044 	{
  4045 		if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
  4046 		{
  4047 			core->sound->stopSfx(dsq->loops.charge);
  4048 			dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
  4049 		}
  4050 
  4051 
  4052 		/*
  4053 		std::ostringstream os;
  4054 		os << "spellCharge: " << spellCharge;
  4055 		debugLog(os.str());
  4056 		*/
  4057 		/*
  4058 		chargeGraphic->alpha.interpolateTo(0, 0.5, 0);
  4059 		chargeGraphic->scale.interpolateTo(Vector(0,0), 1.0, 0);
  4060 		*/
  4061 
  4062 		chargingEmitter->stop();
  4063 
  4064 		charging = false;
  4065 		state.spellCharge = 0;
  4066 	}
  4067 }
  4068 
  4069 Vector Avatar::getWallNormal(TileVector t)
  4070 {
  4071 	return dsq->game->getWallNormal(t.worldVector(), 5)*-1;
  4072 
  4073 	/*
  4074 	Vector accum;
  4075 	int c = 0;
  4076 	for (int x = -2; x <= 2; x++)
  4077 	{
  4078 		for (int y = -2; y <= 2; y++)
  4079 		{
  4080 			TileVector check = t;
  4081 			check.x+=x;
  4082 			check.y+=y;
  4083 			if (!dsq->game->isObstructed(check))
  4084 			{
  4085 				Vector v(check.x, check.y);
  4086 				c++;
  4087 				accum+= v;
  4088 			}
  4089 			//check.x+x, check.y+y
  4090 		}
  4091 	}
  4092 	if (c > 0)
  4093 	{
  4094 		accum /= c;
  4095 		//accum.normalize2D();
  4096 		accum.setLength2D(-1);
  4097 		return accum;
  4098 	}
  4099 	Vector v = vel;
  4100 	v.setLength2D(-1);
  4101 	return v;
  4102 	*/
  4103 }
  4104 
  4105 int Avatar::getSingingInterfaceRadius()
  4106 {
  4107 	return singingInterfaceRadius;
  4108 }
  4109 
  4110 int Avatar::getOpenSingingInterfaceRadius()
  4111 {
  4112 	return openSingingInterfaceRadius;
  4113 }
  4114 
  4115 bool Avatar::isSwimming()
  4116 {
  4117 	return swimming;
  4118 }
  4119 
  4120 void Avatar::lockToWallCommon()
  4121 {
  4122 	swimEmitter.stop();
  4123 
  4124 	skeletalSprite.stopAllAnimations();
  4125 	rotationOffset.interpolateTo(0, 0.01);
  4126 
  4127 	fallGravityTimer = 0;
  4128 
  4129 	dsq->spawnParticleEffect("LockToWall", position);
  4130 	stopBurst();
  4131 	stopRoll();
  4132 	disableOverideMaxSpeed();
  4133 	core->sound->playSfx("LockToWall", 1.0, 0);//, (1000+rand()%100)/1000.0);
  4134 	//bursting = false;
  4135 	animatedBurst = false;
  4136 	this->burst = 1;
  4137 	//lastLockToWallPos = position;
  4138 
  4139 	state.lockToWallDelay.start(0.2);
  4140 	state.lockedToWall = true;
  4141 
  4142 	lockToWallFallTimer = -1;
  4143 
  4144 	// move this to its own function?
  4145 	state.backFlip = false;
  4146 	skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
  4147 }
  4148 
  4149 void Avatar::lockToWall()
  4150 {
  4151 	if (riding) return;
  4152 	if (inCurrent && dsq->continuity.form != FORM_BEAST) return;
  4153 	if (dsq->continuity.form == FORM_FISH || dsq->continuity.form == FORM_SPIRIT) return;
  4154 	if (state.lockedToWall) return;
  4155 	if (vel.x == 0 && vel.y == 0) return;
  4156 	if (dsq->game->isPaused()) return;
  4157 
  4158 	/*
  4159 	Vector opos = position;
  4160 	position = lastPosition;
  4161 	*/
  4162 
  4163 	TileVector t(position);
  4164 	TileVector myTile = t;
  4165 	// 3 + 4
  4166 	// 4 + 5
  4167 	Vector m = vel;
  4168 	m.setLength2D(3);
  4169 	t.x += int(m.x);
  4170 	t.y += int(m.y);
  4171 	/*
  4172 	TileVector t2 = t;
  4173 	m.setLength2D(4);
  4174 	t2.x += int(m.x);
  4175 	t2.y += int(m.y);
  4176 	*/
  4177 	m.setLength2D(2);
  4178 	TileVector tback = myTile;
  4179 	tback.x += int(m.x);
  4180 	tback.y += int(m.y);
  4181 
  4182 	Vector add = m;
  4183 	add.setLength2D(1);
  4184 	TileVector tnext = myTile;
  4185 	tnext.x += int(add.x);
  4186 	tnext.y += int(add.y);
  4187 
  4188 	// find the fraking wall
  4189 	/*
  4190 	TileVector actualWall = myTile;
  4191 	Vector lastWall;
  4192 	Vector getWall(myTile.x, myTile.y);
  4193 	for (int i = -1; i < 5; i++)
  4194 	{
  4195 		getWall.x += add.x*i;
  4196 		getWall.y += add.y*i;
  4197 		if (lastWall.isZero())
  4198 			lastWall = getWall;
  4199 		TileVector test(getWall.x, getWall.y);
  4200 		if (dsq->game->isObstructed(test))
  4201 			break;
  4202 		lastWall = getWall;
  4203 	}
  4204 	actualWall = TileVector(lastWall.x, lastWall.y);
  4205 	*/
  4206 
  4207 	Vector diff = lastLockToWallPos - position;
  4208 
  4209 	bool good = true;
  4210 	if (!dsq->game->isObstructed(t))
  4211 	{
  4212 		int tried = 0;
  4213 //tryAgain:
  4214 		while(1)
  4215 		{
  4216 			TileVector test;
  4217 
  4218 			test = TileVector(t.x, t.y+1);
  4219 			if (dsq->game->isObstructed(test))
  4220 			{
  4221 				t = test;
  4222 				break;
  4223 			}
  4224 			test = TileVector(t.x, t.y-1);
  4225 			if (dsq->game->isObstructed(test))
  4226 			{
  4227 				t = test;
  4228 				break;
  4229 			}
  4230 			test = TileVector(t.x-1, t.y);
  4231 			if (dsq->game->isObstructed(test))
  4232 			{
  4233 				t = test;
  4234 				break;
  4235 			}
  4236 			test = TileVector(t.x+1, t.y);
  4237 			if (dsq->game->isObstructed(test))
  4238 			{
  4239 				t = test;
  4240 				break;
  4241 			}
  4242 			test = TileVector(t.x+1, t.y+1);
  4243 			if (dsq->game->isObstructed(test))
  4244 			{
  4245 				t = test;
  4246 				break;
  4247 			}
  4248 			test = TileVector(t.x-1, t.y+1);
  4249 			if (dsq->game->isObstructed(test))
  4250 			{
  4251 				t = test;
  4252 				break;
  4253 			}
  4254 			test = TileVector(t.x+1, t.y-1);
  4255 			if (dsq->game->isObstructed(test))
  4256 			{
  4257 				t = test;
  4258 				break;
  4259 			}
  4260 			test = TileVector(t.x-1, t.y-1);
  4261 			if (dsq->game->isObstructed(test))
  4262 			{
  4263 				t = test;
  4264 				break;
  4265 			}
  4266 			tried++;
  4267 			//if (tried >= 2)
  4268 			if (true)
  4269 			{
  4270 				good = false;
  4271 				break;
  4272 			}
  4273 			else
  4274 			{
  4275 				//debugLog("trying other");
  4276 				//t = myTile;
  4277 				//goto tryAgain;
  4278 			}
  4279 		}
  4280 	}
  4281 
  4282 	if (dsq->game->getGrid(t)==OT_HURT && dsq->continuity.form != FORM_NATURE)
  4283 	{
  4284 		good = false;
  4285 	}
  4286 	if (good /*&& dsq->game->)isObstructed(t2, OT_BLACK)*/ /*&& diff.getSquaredLength2D() > sqr(40)*/)
  4287 	{
  4288 		wallNormal = dsq->game->getWallNormal(position);
  4289 		bool outOfWaterHit = (!_isUnderWater && !(wallNormal.y < -0.1));
  4290 		if (wallNormal.isZero() ) //|| outOfWaterHit
  4291 		{
  4292 			debugLog("COULD NOT FIND NORMAL, GOING TO BOUNCE");
  4293 			if (outOfWaterHit)
  4294 			{
  4295 				/*
  4296 				Animation *anim = skeletalSprite.getCurrentAnimation();
  4297 				if (anim && anim->name == "hitGround")
  4298 				{
  4299 				}
  4300 				else
  4301 				{
  4302 					skeletalSprite.animate("hitGround");
  4303 				}
  4304 				*/
  4305 			}
  4306 			return;
  4307 		}
  4308 		else
  4309 		{
  4310 
  4311 			/*
  4312 			position = TileVector(position).worldVector();
  4313 			lastPosition = position;
  4314 			*/
  4315 
  4316 			if (!dsq->mod.isActive() && !dsq->continuity.getFlag("lockedToWall"))
  4317 			{
  4318 				
  4319 				if (!dsq->game->isControlHint()){
  4320 					dsq->continuity.setFlag("lockedToWall", 1);
  4321 					dsq->game->setControlHint(dsq->continuity.stringBank.get(13), 1, 0, 0, 6, "", true);
  4322 				}
  4323 			}
  4324 
  4325 			lockToWallCommon();
  4326 
  4327 			if (outOfWaterHit)
  4328 				lockToWallFallTimer = 0.4;
  4329 			else
  4330 				lockToWallFallTimer = -1;
  4331 
  4332 			//wallPushVec = getWallNormal(t);
  4333 
  4334 
  4335 			wallPushVec = wallNormal;
  4336 			wallPushVec *= 2000;
  4337 			wallPushVec.z = 0;
  4338 			skeletalSprite.stopAllAnimations();
  4339 			if (wallPushVec.y < 0 && (fabs(wallPushVec.y) > fabs(wallPushVec.x)))
  4340 			{
  4341 				skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
  4342 			}
  4343 			else
  4344 			{
  4345 				skeletalSprite.transitionAnimate("wall", 0.2, -1);
  4346 			}
  4347 			rotateToVec(wallPushVec, 0.1);
  4348 
  4349 			offset.stop();
  4350 
  4351 			Vector goIn;
  4352 
  4353 			TileVector uset;
  4354 			if (!dsq->game->isObstructed(tnext))
  4355 			{
  4356 				uset = tnext;
  4357 			}
  4358 			else
  4359 				uset = tback;
  4360 
  4361 			int tileType = dsq->game->getGrid(t);
  4362 			Vector offdiff = t.worldVector() - position;
  4363 			if (!offdiff.isZero())
  4364 			{
  4365 				if (tileType != OT_INVISIBLEIN)
  4366 				{
  4367 					Vector adjust = offdiff;
  4368 					adjust.setLength2D(TILE_SIZE*2);
  4369 					offdiff -= adjust;
  4370 				}
  4371 				else
  4372 				{
  4373 					Vector adjust = offdiff;
  4374 					adjust.setLength2D(TILE_SIZE/2);
  4375 					offdiff -= adjust;
  4376 				}
  4377 			}
  4378 
  4379 			float spd = vel.getLength2D();
  4380 			if (spd < 1000)
  4381 				spd = 1000;
  4382 
  4383 			Vector diff = offset - offdiff;
  4384 			float len = diff.getLength2D();
  4385 			float time = 0;
  4386 			if (len > 0)
  4387 			{
  4388 				time = len/spd;
  4389 			}
  4390 			/*
  4391 			std::ostringstream os;
  4392 			os << "time: " << time;
  4393 			debugLog(os.str());
  4394 			*/
  4395 			offset.interpolateTo(offdiff, time);
  4396 			/*
  4397 			if (tileType == OT_INVISIBLEIN)
  4398 			{
  4399 				goIn = wallNormal;
  4400 				goIn.setLength2D(-28);
  4401 			}
  4402 			else
  4403 			{
  4404 				Vector diff = uset.worldVector()-position;
  4405 				goIn = diff;
  4406 			}
  4407 			offset.interpolateTo(goIn, 0.05);
  4408 			*/
  4409 
  4410 			wallLockTile = t;
  4411 
  4412 			vel = Vector(0,0,0);
  4413 			vel2 = 0;
  4414 
  4415 			/*
  4416 			Vector oldPos = position;
  4417 
  4418 			while (true)
  4419 			{
  4420 				Vector m = vel;
  4421 				m |= 1;
  4422 				position += m;
  4423 				TileVector t(position);
  4424 				if (dsq->game->isObstructed(t, OT_BLACK))
  4425 				{
  4426 					position = oldPos;
  4427 					break;
  4428 				}
  4429 			}
  4430 			*/
  4431 		}
  4432 	}
  4433 	else
  4434 	{
  4435 		//debugLog("COULD NOT FIND TILE TO GRAB ONTO");
  4436 		//position = opos;
  4437 	}
  4438 }
  4439 
  4440 void Avatar::applyTripEffects()
  4441 {
  4442 	color.interpolateTo(BLIND_COLOR, 0.5);
  4443 	currentColor = BLIND_COLOR;
  4444 
  4445 	tripper->alpha.interpolateTo(1, 8);
  4446 
  4447 	tripper->color = Vector(1, 1, 1);
  4448 	tripper->rotation.z = 0;
  4449 	tripper->rotation.interpolateTo(Vector(0, 0, 360), 10, -1);
  4450 	tripper->scale = Vector(1.25, 1.25, 1.25);
  4451 	tripper->scale.interpolateTo(Vector(1.3, 1.3, 1.3), 2, -1, 1, 1);
  4452 
  4453 	if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
  4454 	{
  4455 		dsq->sound->stopSfx(dsq->loops.trip);
  4456 		dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
  4457 	}
  4458 
  4459 	PlaySfx play;
  4460 	play.name = "TripLoop";
  4461 	play.loops = -1;
  4462 	play.fade = SFT_IN;
  4463 	play.time = 1;
  4464 	dsq->loops.trip = dsq->sound->playSfx(play);
  4465 }
  4466 
  4467 void Avatar::removeTripEffects()
  4468 {
  4469 	color.interpolateTo(Vector(1,1,1),0.5);
  4470 	currentColor = Vector(1,1,1);
  4471 	tripper->alpha.interpolateTo(0, 4);
  4472 
  4473 	if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
  4474 	{
  4475 		dsq->sound->fadeSfx(dsq->loops.trip, SFT_OUT, 3);
  4476 		dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
  4477 	}
  4478 }
  4479 
  4480 void Avatar::applyBlindEffects()
  4481 {
  4482 	// screen black
  4483 
  4484 	// character black
  4485 	color.interpolateTo(BLIND_COLOR, 0.5);
  4486 	currentColor = BLIND_COLOR;
  4487 	blinder->alpha.interpolateTo(1, 0.5);
  4488 
  4489 	blinder->rotation.z = 0;
  4490 	blinder->rotation.interpolateTo(Vector(0, 0, 360), 10, -1);
  4491 	blinder->scale = Vector(1.25, 1.25, 1.25);
  4492 	blinder->scale.interpolateTo(Vector(1.3, 1.3, 1.3), 2, -1, 1, 1);
  4493 
  4494 	//dsq->toggleMuffleSound(1);
  4495 }
  4496 
  4497 void Avatar::removeBlindEffects()
  4498 {
  4499 	color.interpolateTo(Vector(1,1,1),0.5);
  4500 	currentColor = Vector(1,1,1);
  4501 	blinder->alpha.interpolateTo(0, 0.5);
  4502 	//dsq->toggleMuffleSound(0);
  4503 }
  4504 
  4505 void Avatar::setBlind(float time)
  4506 {
  4507 	if (time == 0)
  4508 	{
  4509 		removeBlindEffects();
  4510 		return;
  4511 	}
  4512 
  4513 	if (!state.blind)
  4514 	{
  4515 		applyBlindEffects();
  4516 	}
  4517 	state.blind = true;
  4518 	if (time > state.blindTimer.getValue())
  4519 		///*state.blindTimer.getValue() + */
  4520 		state.blindTimer.start(time);
  4521 }
  4522 
  4523 void Avatar::setNearestPullTarget()
  4524 {
  4525 	int smallestDist = -1;
  4526 	int maxDist = 800;
  4527 	Entity *closest = 0;
  4528 	FOR_ENTITIES(i)
  4529 	{
  4530 		Entity *e = *i;
  4531 		if (e)
  4532 		{
  4533 			if ((e->isPullable()) && e->life == 1)
  4534 			{
  4535 				float dist = (e->position - position).getSquaredLength2D();
  4536 				if (dist < sqr(maxDist) && (smallestDist == -1 || dist < smallestDist))
  4537 				{
  4538 					closest = e;
  4539 					smallestDist = dist;
  4540 				}
  4541 			}
  4542 		}
  4543 	}
  4544 	if (closest)
  4545 	{
  4546 		pullTarget = closest;
  4547 		pullTarget->startPull();
  4548 	}
  4549 }
  4550 
  4551 void Avatar::openPullTargetInterface()
  4552 {
  4553 	debugLog("Open pull target");
  4554 	if (pullTarget)
  4555 	{
  4556 		pullTarget->stopPull();
  4557 	}
  4558 	pullTarget = 0;
  4559 	potentialPullTarget = 0;
  4560 	pickingPullTarget = true;
  4561 	// change the cursor
  4562 	dsq->cursor->color = Vector(0.5,0.5,1);
  4563 }
  4564 
  4565 void Avatar::closePullTargetInterface()
  4566 {
  4567 	debugLog("close pull target");
  4568 	pickingPullTarget = false;
  4569 	potentialPullTarget = 0;
  4570 	dsq->cursor->color = Vector(1,1,1);
  4571 }
  4572 
  4573 void Avatar::createWeb()
  4574 {
  4575 	web = new Web;
  4576 	web->setParentEntity(this);
  4577 	dsq->game->addRenderObject(web, LR_ENTITIES);
  4578 	curWebPoint = web->addPoint(dsq->game->avatar->position);
  4579 	curWebPoint = web->addPoint(dsq->game->avatar->position);
  4580 }
  4581 
  4582 void Avatar::clearWeb()
  4583 {
  4584 	if (web)
  4585 	{
  4586 		web->setExistence(25);
  4587 		//web->setLife(1);
  4588 		//web->setDecayRate(1.0f/30.0f);
  4589 		//web->fadeAlphaWithLife = 1;
  4590 
  4591 		web = 0;
  4592 	}
  4593 }
  4594 
  4595 Avatar::Avatar() : Entity(), ActionMapper()
  4596 {
  4597 	canDie = true;
  4598 	
  4599 	urchinDelay = 0;
  4600 	jellyDelay = 0;
  4601 	warpIn = false;
  4602 #ifdef AQ_TEST_QUADTRAIL
  4603 	quadTrail = new QuadTrail(100, 32);
  4604 	quadTrail->setTexture("Particles/QuadTrail");
  4605 	quadTrail->setBlendType(BLEND_ADD);
  4606 	dsq->game->addRenderObject(quadTrail, LR_PARTICLES);
  4607 #endif
  4608 
  4609 	curWebPoint = 0;
  4610 
  4611 	web = 0;
  4612 
  4613 	lastBurstType = BURST_NONE;
  4614 	dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
  4615 	leftHandEmitter = rightHandEmitter = 0;
  4616 	boneLeftHand = boneRightHand = 0;
  4617 	canChangeForm = true;
  4618 	biteTimer = 0;
  4619 	dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
  4620 	//heartbeat = 0;
  4621 
  4622 	lastNote = -1;
  4623 	headTextureTimer = 0;
  4624 	bone_dualFormGlow = 0;
  4625 	//dsq->continuity.dualFormCharge = 0;
  4626 	//dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
  4627 	debugLog("Avatar 1");
  4628 
  4629 	//registerEntityDied = true;
  4630 	setv(EV_ENTITYDIED, 1);
  4631 	wallJumps = 0;
  4632 	wallBurstTimer = 0;
  4633 	beautyFlip = false;
  4634 	invincibleBreak = true;
  4635 	targetUpdateDelay = 0;
  4636 	biteDelay = 0;
  4637 	songInterfaceTimer = 0;
  4638 	quickSongCastDelay = 0;
  4639 	flourish = false;
  4640 	tummyAmount=0;
  4641 
  4642 	blockSinging = false;
  4643 	singing = false;
  4644 
  4645 	spiritEnergyAbsorbed = 0;
  4646 	joystickMove = false;
  4647 
  4648 	debugLog("setCanLeaveWater");
  4649 
  4650 	setCanLeaveWater(true);
  4651 
  4652 	debugLog("setOverrideRenderPass");
  4653 
  4654 	setOverrideRenderPass(1);
  4655 
  4656 	debugLog("Done those");
  4657 	/*
  4658 	setRenderPass(2);
  4659 	*/
  4660 	rippleDelay = 0;
  4661 	ripples = false;
  4662 	fallGravityTimer = 0;
  4663 	lastOutOfWaterMaxSpeed = 0;
  4664 	//chargeGraphic = 0;
  4665 	tummyTimer = 0;
  4666 	inTummy = EAT_NONE;
  4667 	ropeTimer = shieldPoints = auraTimer = 0;
  4668 	glow = 0;
  4669 
  4670 	fireDelay = 0;
  4671 	inFormInterface = false;
  4672 	looking = false;
  4673 	canWarpDelay = 0.5;
  4674 	canWarp = false;
  4675 	rollDidOne = 0;
  4676 	lastQuad = lastQuadDir = rollDelay = rolling = 0;
  4677 	stopTimer = 0;
  4678 	doubleClickDelay = 0;
  4679 	damageDelay = 0;
  4680 	didShockDamage = false;
  4681 	chargeLevelAttained = 0;
  4682 	shockTimer = 0;
  4683 	activeAura = AURA_NONE;
  4684 	ropeState = 0;
  4685 	movingOn = false;
  4686 	currentMaxSpeed = 0;
  4687 	abilityCharging = -1;
  4688 	pickingPullTarget = false;
  4689 	potentialPullTarget = 0;
  4690 	pullTarget = 0;
  4691 	revertTimer = 0;
  4692 	currentSongIdx = -1;
  4693 
  4694 
  4695 	debugLog("Avatar vars->");
  4696 
  4697 	damageTime = vars->avatarDamageTime;
  4698 
  4699 	activateEntity = 0;
  4700 	canMove = true;
  4701 	castShockTimer = 0;
  4702 	//scale = Vector(0.5, 0.5);
  4703 	scale = Vector(0.5, 0.5);
  4704 
  4705 	debugLog("Avatar 2");
  4706 	//scale = Vector(1.0, 1.0);
  4707 	//setTexture("Naija-sprite2");
  4708 	renderQuad = false;
  4709 	name = "Naija";
  4710 	setEntityType(ET_AVATAR);
  4711 
  4712 	targets.resize(1);
  4713 
  4714 
  4715 	lastEntityActivation = 0;
  4716 
  4717 	entityToActivate = 0;
  4718 	pathToActivate = 0;
  4719 	zoomOverriden = false;
  4720 	canWarp = true;
  4721 	disableConversationStart = false;
  4722 	blinder = 0;
  4723 	zoomVel = 0;
  4724 
  4725 	myZoom = Vector(1,1);
  4726 	spellChargeMin = spellCastDelay = 0;
  4727 	this->pushingOffWallEffect = 0;
  4728 	lockToWallFallTimer  = 0;
  4729 	swimming = false;
  4730 	dodgeDelay = 0;
  4731 	charging = false;
  4732 	bursting = false;
  4733 	animatedBurst = false;
  4734 	burst = 1;
  4735 	burstDelay = 0;
  4736 	ignoreInputDelay = 0;
  4737 	idleAnimDelay = 2;
  4738 	avatar = this;
  4739 
  4740 	frame = 0;
  4741 
  4742 	particleDelay = 0;
  4743 
  4744 	swimming = false;
  4745 
  4746 	debugLog("Avatar 3");
  4747 	hair = new Hair();
  4748 	hair->setTexture("Naija/Cape");
  4749 	hair->setOverrideRenderPass(1);
  4750 	hair->setRenderPass(1);
  4751 	dsq->game->addRenderObject(hair, LR_ENTITIES);
  4752 
  4753 	debugLog("Avatar 4");
  4754 
  4755 	bindInput();
  4756 
  4757 	debugLog("Avatar 5");
  4758 
  4759 	blinder = new PauseQuad;
  4760 	blinder->position = Vector(400, 300, 4.5);
  4761 	blinder->setTexture("particles/blinder");
  4762 	//blinder->width = blinder->height = 810;
  4763 	blinder->autoWidth = AUTO_VIRTUALWIDTH;
  4764 	blinder->autoHeight = AUTO_VIRTUALWIDTH;
  4765 	blinder->scale = Vector(1.0125,1.0125);
  4766 	blinder->followCamera = 1;
  4767 
  4768 	blinder->alpha = 0;
  4769 	dsq->game->addRenderObject(blinder, LR_AFTER_EFFECTS);
  4770 
  4771 	tripper = new PauseQuad;
  4772 	tripper->position = Vector(400,300);
  4773 	tripper->setTexture("particles/tripper");
  4774 	//tripper->setWidthHeight(810, 810);
  4775 	tripper->autoWidth = AUTO_VIRTUALWIDTH;
  4776 	tripper->autoHeight = AUTO_VIRTUALWIDTH;
  4777 	tripper->scale = Vector(1.0125, 1.0125);
  4778 	tripper->followCamera = 1;
  4779 	tripper->alpha = 0;
  4780 	dsq->game->addRenderObject(tripper, LR_AFTER_EFFECTS);
  4781 
  4782 	songIcons.resize(8);
  4783 	int i = 0;
  4784 	for (i = 0; i < songIcons.size(); i++)
  4785 	{
  4786 		songIcons[i] = new SongIcon(i);
  4787 		songIcons[i]->alpha = 0;
  4788 		songIcons[i]->followCamera = 1;
  4789 		dsq->game->addRenderObject(songIcons[i], LR_HUD);
  4790 	}
  4791 
  4792 	setSongIconPositions();
  4793 
  4794 	fader = new Quad;
  4795 	fader->position = Vector(400,300);
  4796 	fader->setTexture("fader");
  4797 	fader->setWidthHeight(core->getVirtualWidth()+10);
  4798 	fader->followCamera = 1;
  4799 	fader->alpha = 0;
  4800 	dsq->game->addRenderObject(fader, LR_AFTER_EFFECTS);
  4801 
  4802 	text = 0;
  4803 
  4804 	burstBar = 0;
  4805 
  4806 	/*
  4807 	chargeGraphic = new Particle;
  4808 	{
  4809 		chargeGraphic->setBlendType(RenderObject::BLEND_ADD);
  4810 		chargeGraphic->setTexture("glow");
  4811 		chargeGraphic->alpha = 0;
  4812 		//chargeGraphic->color = Vector(1,,0);
  4813 		chargeGraphic->width = 128;
  4814 		chargeGraphic->height = 128;
  4815 		chargeGraphic->scale = Vector(0,0);
  4816 		chargeGraphic->parentManagedPointer = 1;
  4817 		//chargeGraphic->positionSnapTo = &this->position;
  4818 		chargeGraphic->rotation.interpolateTo(Vector(0,0,360), 1, -1, 1);
  4819 		chargeGraphic->position = Vector(16, 58);
  4820 		//chargeGraphic->color = Vector(1,0.2,0.1);
  4821 	}
  4822 	//skeletalSprite.getBoneByIdx(3)->addChild(chargeGraphic);
  4823 	*/
  4824 
  4825 	debugLog("Avatar 6");
  4826 
  4827 	targetQuads.resize(targets.size());
  4828 
  4829 	for (i = 0; i < targets.size(); i++)
  4830 	{
  4831 		targetQuads[i] = new ParticleEffect;
  4832 		/*
  4833 		targetQuads[i]->setTexture("missingImage"); 
  4834 		targetQuads[i]->alpha = 0;
  4835 		*/
  4836 		targetQuads[i]->load("EnergyBlastTarget");
  4837 
  4838 		// HACK: should have its own layer?
  4839 		dsq->game->addRenderObject(targetQuads[i], LR_PARTICLES);
  4840 	} 
  4841 
  4842 	lightFormGlow = new Quad("Naija/LightFormGlow", 0);
  4843 	lightFormGlow->alpha = 0;
  4844 	
  4845 	lightFormGlow->scale.interpolateTo(Vector(5.5, 5.5), 0.4, -1, 1);
  4846 	//lightFormGlow->positionSnapTo = &position;
  4847 	dsq->game->addRenderObject(lightFormGlow, LR_ELEMENTS13);
  4848 
  4849 	lightFormGlowCone = new Quad("Naija/LightFormGlowCone", 0);
  4850 	lightFormGlowCone->alpha = 0;
  4851 	lightFormGlowCone->scale = Vector(1, 6); // 4.5
  4852 	dsq->game->addRenderObject(lightFormGlowCone, LR_ELEMENTS13);
  4853 
  4854 
  4855 	debugLog("Avatar 7");
  4856 
  4857 	addChild(&regenEmitter, PM_STATIC);
  4858 	regenEmitter.load("FoodEffectRegen");
  4859 
  4860 	addChild(&speedEmitter, PM_STATIC);
  4861 	speedEmitter.load("FoodEffectSpeed");
  4862 
  4863 	addChild(&defenseEmitter, PM_STATIC);
  4864 	defenseEmitter.load("FoodEffectDefense");
  4865 
  4866 	addChild(&invincibleEmitter, PM_STATIC);
  4867 	invincibleEmitter.load("FoodEffectInvincible");
  4868 
  4869 	addChild(&auraEmitter, PM_STATIC);
  4870 
  4871 	addChild(&auraHitEmitter, PM_STATIC);
  4872 	auraHitEmitter.load("AuraShieldHit");
  4873 
  4874 	chargingEmitter = new ParticleEffect;
  4875 	dsq->getTopStateData()->addRenderObject(chargingEmitter, LR_PARTICLES);
  4876 	/*
  4877 	addChild(&chargingEmitter);
  4878 	chargingEmitter.parentManagedStatic = true;
  4879 	*/
  4880 
  4881 	chargeEmitter = new ParticleEffect;
  4882 	dsq->getTopStateData()->addRenderObject(chargeEmitter, LR_PARTICLES_TOP);
  4883 
  4884 	leftHandEmitter = new ParticleEffect;
  4885 	dsq->getTopStateData()->addRenderObject(leftHandEmitter, LR_PARTICLES);
  4886 
  4887 	rightHandEmitter = new ParticleEffect;
  4888 	dsq->getTopStateData()->addRenderObject(rightHandEmitter, LR_PARTICLES);
  4889 
  4890 	/*
  4891 	leftHandEmitter = new ParticleEffect;
  4892 	dsq->getTopStateData()->addRenderObject(`, LR_PARTICLES);
  4893 
  4894 	rightHandEmitter = new ParticlesEffect;
  4895 	dsq->getTopStateData()->addRenderObject(rightHandEmitter, LR_PARTICLES);
  4896 	*/
  4897 
  4898 	/*
  4899 	addChild(&chargeEmitter);
  4900 	chargeEmitter.parentManagedStatic = true;
  4901 	*/
  4902 
  4903 	addChild(&biteLeftEmitter, PM_STATIC);
  4904 	biteLeftEmitter.load("BiteLeft");
  4905 
  4906 	addChild(&biteRightEmitter, PM_STATIC);
  4907 	biteRightEmitter.load("BiteRight");
  4908 
  4909 	addChild(&wakeEmitter, PM_STATIC);
  4910 	wakeEmitter.load("Wake");
  4911 
  4912 	addChild(&swimEmitter, PM_STATIC);
  4913 	swimEmitter.load("Swim");
  4914 
  4915 	addChild(&plungeEmitter, PM_STATIC);
  4916 	plungeEmitter.load("Plunge");
  4917 	plungeEmitter.position = Vector(0,-100);
  4918 
  4919 	addChild(&spiritBeaconEmitter, PM_STATIC);
  4920 	spiritBeaconEmitter.load("SpiritBeacon");
  4921 
  4922 	addChild(&healEmitter, PM_STATIC);
  4923 
  4924 	addChild(&hitEmitter, PM_STATIC);
  4925 
  4926 	addChild(&rollLeftEmitter, PM_STATIC);
  4927 
  4928 	addChild(&rollRightEmitter, PM_STATIC);
  4929 
  4930 	rollRightEmitter.load("RollRight");
  4931 	rollLeftEmitter.load("RollLeft");
  4932 
  4933 	debugLog("Avatar 8");
  4934 
  4935 	perform(STATE_IDLE);
  4936 	skeletalSprite.animate(getIdleAnimName(),-1);
  4937 
  4938 	debugLog("Avatar 9");
  4939 
  4940 	if (dsq->useMic)
  4941 	{
  4942 		debugLog("useMic...initing recording");
  4943 
  4944 		/*
  4945 		debugLog("RecordStart...");
  4946 		avatarRecord = BASS_RecordStart(44100,1,0,&recordCallback,0);
  4947 
  4948 		if (avatarRecord)
  4949 		{
  4950 			if (dsq->autoSingMenuOpen)
  4951 			{
  4952 			}
  4953 			else
  4954 			{
  4955 				debugLog("ChannelPause...");
  4956 				BASS_ChannelPause(avatarRecord);
  4957 			}
  4958 		}
  4959 		debugLog("...done");
  4960 		*/
  4961 	}
  4962 
  4963 	debugLog("Avatar 10");
  4964 	setDamageTarget(DT_AVATAR_LANCE, false);
  4965 
  4966 	//changeForm(FORM_NORMAL, false);
  4967 
  4968 	refreshNormalForm();
  4969 
  4970 }
  4971 
  4972 void Avatar::revert()
  4973 {
  4974 	if (canChangeForm)
  4975 	{
  4976 		if (dsq->continuity.form != FORM_NORMAL)
  4977 			changeForm(FORM_NORMAL);
  4978 	}
  4979 }
  4980 
  4981 void Avatar::onHeal(int type)
  4982 {
  4983 	if (type == 1)
  4984 	{
  4985 		healEmitter.load("Heal");
  4986 		healEmitter.start();
  4987 	}
  4988 }
  4989 
  4990 void Avatar::refreshNormalForm()
  4991 {
  4992 	std::string c = dsq->continuity.costume;
  4993 	if (c.empty())
  4994 		c = "Naija";
  4995 	refreshModel("Naija", c);
  4996 	if (true)
  4997 	{
  4998 		if (hair)
  4999 			hair->alphaMod = 1.0;
  5000 		if (!c.empty() && c!="Naija")
  5001 		{
  5002 			if (exists(core->getBaseTextureDirectory() + "naija/cape-"+c+".png"))
  5003 			{
  5004 				if (hair)
  5005 					hair->setTexture("naija/cape-"+c);
  5006 			}
  5007 			else
  5008 			{
  5009 				if (hair)
  5010 					hair->alphaMod = 0;
  5011 			}
  5012 		}
  5013 		else
  5014 		{
  5015 			if (hair)
  5016 				hair->setTexture("naija/cape");
  5017 		}
  5018 	}
  5019 	else
  5020 	{
  5021 		if (hair)
  5022 			hair->alphaMod = 0.0;
  5023 	}
  5024 }
  5025 
  5026 void Avatar::refreshModel(std::string file, const std::string &skin, bool forceIdle)
  5027 {
  5028 	stringToLower(file);
  5029 
  5030 	bool loadedSkeletal = false;
  5031 
  5032 	if (!skeletalSprite.isLoaded() || nocasecmp(skeletalSprite.filenameLoaded, file)!=0)
  5033 	{
  5034 		skeletalSprite.loadSkeletal(file);
  5035 		loadedSkeletal = true;
  5036 	}
  5037 	if (!skin.empty())
  5038 		skeletalSprite.loadSkin(skin);
  5039 
  5040 	if (file == "beast")
  5041 	{
  5042 		skeletalSprite.scale = Vector(1.25,1.25);
  5043 	}
  5044 	else
  5045 		skeletalSprite.scale = Vector(1,1);
  5046 
  5047 	Animation *anim = skeletalSprite.getCurrentAnimation(0);
  5048 	if (forceIdle || (!anim || loadedSkeletal))
  5049 	{
  5050 		idle();
  5051 	}
  5052 
  5053 	if (file == "naija")
  5054 	{
  5055 		bone_head = skeletalSprite.getBoneByIdx(1);
  5056 		boneRightFoot = skeletalSprite.getBoneByName("RightFoot");
  5057 		boneLeftFoot = skeletalSprite.getBoneByName("LeftFoot");
  5058 		boneRightArm = skeletalSprite.getBoneByName("RightArm");
  5059 		boneLeftArm = skeletalSprite.getBoneByName("LeftArm");
  5060 		boneFish2 = skeletalSprite.getBoneByName("Fish2");
  5061 		boneFish2->alpha = 0;
  5062 		bone_dualFormGlow = skeletalSprite.getBoneByName("DualFormGlow");
  5063 		bone_dualFormGlow->scale = 0;
  5064 		bone_dualFormGlow->setBlendType(BLEND_ADD);
  5065 
  5066 		boneLeftHand = skeletalSprite.getBoneByName("LeftArm");
  5067 		boneRightHand = skeletalSprite.getBoneByName("RightArm");
  5068 	}
  5069 	else
  5070 	{
  5071 		bone_dualFormGlow = 0;
  5072 		bone_head = 0;
  5073 		boneRightFoot = boneLeftFoot = boneRightArm = boneLeftArm = boneFish2 = skeletalSprite.getBoneByIdx(0);
  5074 		boneLeftHand = boneRightHand = 0;
  5075 	}
  5076 
  5077 	core->resetTimer();
  5078 
  5079 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
  5080 }
  5081 
  5082 Avatar::~Avatar()
  5083 {
  5084 	songIcons.clear();
  5085 }
  5086 
  5087 void Avatar::destroy()
  5088 {
  5089 	Entity::destroy();
  5090 
  5091 	text = 0;
  5092 
  5093 	if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
  5094 	{
  5095 		core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
  5096 		dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
  5097 	}
  5098 	if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
  5099 	{
  5100 		core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
  5101 		dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
  5102 	}
  5103 }
  5104 
  5105 void Avatar::fireRope()
  5106 {
  5107 	ropeVel = getAim();
  5108 	ropeVel.z = 0;
  5109 	if (!ropeVel.isLength2DIn(1))
  5110 	{
  5111 		ropeTimer = 0.2;
  5112 		ropeState = 1;
  5113 		ropePos = position;
  5114 		//ropeVel = core->mouse.position - Vector(400,300);
  5115 
  5116 		ropeVel.setLength2D(7000);
  5117 		//ropeVel |= 500;
  5118 	}
  5119 	else
  5120 		ropeVel = Vector(0,0,0);
  5121 }
  5122 
  5123 void Avatar::toggleZoom()
  5124 {
  5125 	if (core->globalScale.isInterpolating()) return;
  5126 	if (core->globalScale.x == 1)
  5127 		core->globalScale.interpolateTo(Vector(0.75,0.75),0.2);
  5128 	else if (core->globalScale.x == 0.75)
  5129 		core->globalScale.interpolateTo(Vector(0.5,0.5),0.2);
  5130 	else if (core->globalScale.x == 0.5)
  5131 		core->globalScale.interpolateTo(Vector(0.25,0.25),0.2);
  5132 	else if (core->globalScale.x == 0.25)
  5133 		core->globalScale.interpolateTo(Vector(1,1),0.2);
  5134 
  5135 	/*
  5136 	else if (core->globalScale.x == 1.5)
  5137 		core->globalScale.interpolateTo(Vector(1,1),0.2);
  5138 	*/
  5139 
  5140 }
  5141 
  5142 /*
  5143 void Avatar::setActiveSpell(Spells spell)
  5144 {
  5145 	activeSpell = spell;
  5146 }
  5147 */
  5148 
  5149 void Avatar::dodge(std::string dir)
  5150 {
  5151 	if (bursting) return;
  5152 	if (!canMove) return;
  5153 	if (dodgeDelay == 0)
  5154 	{
  5155 		Vector mov;
  5156 
  5157 		if (dir == "right")
  5158 			mov = Vector(1,0);
  5159 		else if (dir == "left")
  5160 			mov = Vector(-1, 0);
  5161 		else if (dir == "down")
  5162 			mov = Vector(0, 1);
  5163 		else if (dir == "up")
  5164 			mov = Vector(0, -1);
  5165 
  5166 		Vector lastPosition = position;
  5167 		//position += mov * 80;
  5168 
  5169 		dodgeVec = mov * 8000;
  5170 		vel += mov * vars->maxDodgeSpeed;
  5171 		//dodgeEffectTimer = 0.125;
  5172 		state.dodgeEffectTimer.start(/*0.125*/vars->dodgeTime);
  5173 		/*
  5174 		float vlen = vel.getLength2D();
  5175 		mov |= 500;
  5176 		vel += mov;
  5177 		*/
  5178 		/*
  5179 		if (dsq->game->collideCircleWithGrid(position, 24))
  5180 		{
  5181 			position = lastPosition;
  5182 		}
  5183 		*/
  5184 	}
  5185 }
  5186 
  5187 void Avatar::startBackFlip()
  5188 {
  5189 	if (boneLock.on) return;
  5190 	if (riding) return;
  5191 
  5192 	skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip", 0.2, 0);
  5193 	vel.x = -vel.x*0.25;
  5194 	state.backFlip = true;
  5195 }
  5196 
  5197 void Avatar::stopBackFlip()
  5198 {
  5199 	if (state.backFlip)
  5200 	{
  5201 		//skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
  5202 		skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip2", 0.2, 0);
  5203 		state.backFlip = false;
  5204 	}
  5205 }
  5206 
  5207 void Avatar::startBurstCommon()
  5208 {
  5209 	skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
  5210 
  5211 	bittenEntities.clear();
  5212 	flourish = false;
  5213 	if (dsq->continuity.form == FORM_BEAST)
  5214 	{
  5215 		setHeadTexture("Bite");
  5216 	}
  5217 	burstTimer = 0;
  5218 
  5219 	setBoneLock(BoneLock());
  5220 
  5221 	biteTimer = 0;
  5222 
  5223 	if (dsq->continuity.form == FORM_BEAST)
  5224 	{
  5225 		if (isfh())
  5226 			biteRightEmitter.start();
  5227 		else
  5228 			biteLeftEmitter.start();
  5229 	}
  5230 }
  5231 
  5232 void Avatar::startBurst()
  5233 {
  5234 	//getVectorToCursorFromScreenCentre()
  5235 	//!bursting && burst == 1
  5236 	//&&
  5237 	/*
  5238 	bool nearWallProblem = false;
  5239 	if (vel.isLength2DIn(200))
  5240 	{
  5241 		if ()
  5242 		{
  5243 			nearWallProblem = false
  5244 		}
  5245 		else
  5246 		{
  5247 			nearWallProblem = true;
  5248 		}
  5249 	}
  5250 	*/
  5251 	//&& !vel.isLength2DIn(32)
  5252 	if (!riding && dsq->continuity.form != FORM_SPIRIT && (joystickMove || getVectorToCursor().getSquaredLength2D() > sqr(BURST_DISTANCE))
  5253 		&& getState() != STATE_PUSH && (!skeletalSprite.getCurrentAnimation() || (skeletalSprite.getCurrentAnimation()->name != "spin"))
  5254 		&& _isUnderWater && !isActing(ACTION_ROLL))
  5255 	{
  5256 		if (!bursting && burst == 1)
  5257 		{
  5258 			dsq->rumble(0.2, 0.2, 0.2);
  5259 			if (dsq->continuity.form != FORM_BEAST)
  5260 				wakeEmitter.start();
  5261 			dsq->game->playBurstSound(pushingOffWallEffect>0);
  5262 			skeletalSprite.animate(getBurstAnimName(), 0);
  5263 			bursting = true;
  5264 			burst = 1.0;
  5265 			ripples = true;
  5266 			startBurstCommon();
  5267 
  5268 			lastBurstType = BURST_NORMAL;
  5269 		}
  5270 		else if (bursting && burstTimer > 0.3)
  5271 		{
  5272 			if (!flourish && !state.nearWall)
  5273 				//&& dsq->continuity.form == FORM_NORMAL)
  5274 			{
  5275 				//if (rand()%100 < 50)
  5276 				if (true)
  5277 				{
  5278 					startFlourish();
  5279 				}
  5280 				/*
  5281 				else
  5282 				{
  5283 					skeletalSprite.transitionAnimate("flourish2", 0.1, 0, 3);
  5284 					flourish = true;
  5285 					if (this->isfh())
  5286 						rotationOffset = Vector(0,0,-360);
  5287 					else
  5288 						rotationOffset = Vector(0,0,360);
  5289 					rotationOffset.interpolateTo(Vector(0,0,0), 0.8, 0, 0, 1);
  5290 				}
  5291 				*/
  5292 				//burst += 0.1;
  5293 				if (!vel.isZero())
  5294 				{
  5295 					Vector add = vel;
  5296 					add.setLength2D(50);
  5297 					vel2 += add;
  5298 				}
  5299 			}
  5300 		}
  5301 	}
  5302 }
  5303 
  5304 void Avatar::startWallBurst(bool useCursor)
  5305 {
  5306 	//if (!bursting && burst == 1 )
  5307 	{
  5308 		Vector goDir;
  5309 
  5310 		//goDir = getVectorToCursorFromScreenCentre();
  5311 		goDir = getVectorToCursor();
  5312 
  5313 		if (goDir.isLength2DIn(BURST_DISTANCE))
  5314 		{
  5315 			if (!goDir.isLength2DIn(minMouse))
  5316 				fallOffWall();
  5317 			return;
  5318 		}
  5319 		goDir.normalize2D();
  5320 
  5321 		if (_isUnderWater && dsq->continuity.form != FORM_BEAST)
  5322 			wakeEmitter.start();
  5323 
  5324 		offset.interpolateTo(Vector(0,0), 0.05);
  5325 
  5326 		dsq->spawnParticleEffect("WallBoost", position+offset, rotation.z);
  5327 		if (goDir.x != 0 || goDir.y != 0)
  5328 		{
  5329 			lastBurstType = BURST_WALL;
  5330 			/*
  5331 			if (wallBurstTimer > 0)
  5332 			{
  5333 				Vector wallJumpDir = position - lastWallJumpPos;
  5334 				if (lastWallJumpPos.isZero() || wallJumpDir.dot2D(lastWallJumpPos) > 0.2)
  5335 				{
  5336 					std::ostringstream os;
  5337 					os << "wallJumps: " << wallJumps;
  5338 					debugLog(os.str());
  5339 					wallJumps++;
  5340 				}
  5341 				else
  5342 				{
  5343 					debugLog("failed angle");
  5344 					stopWallJump();
  5345 				}
  5346 			}
  5347 			wallBurstTimer = 0.8 - 0.1 * wallJumps;
  5348 			if (wallBurstTimer <= 0)
  5349 			{
  5350 				// super boost!
  5351 				debugLog("super boost!");
  5352 				wallJumps = 0;
  5353 			}
  5354 
  5355 
  5356 			lastWallJumpPos = position;
  5357 			lastWallJumpDir = position - lastWallJumpPos;
  5358 			*/
  5359 
  5360 			dsq->rumble(0.22, 0.22, 0.2);
  5361 			bittenEntities.clear();
  5362 			if (useCursor)
  5363 			{
  5364 				wallPushVec = (goDir*0.75 + wallNormal*0.25);
  5365 				//wallPushVec = goDir;
  5366 			}
  5367 			else
  5368 			{
  5369 				float v = goDir.dot2D(wallNormal);
  5370 				if (v <= -0.8)
  5371 					wallPushVec = wallNormal;
  5372 				else
  5373 				{
  5374 					wallPushVec = (goDir*0.5 + wallNormal*0.5);	
  5375 				}
  5376 			}
  5377 				//wallPushVec = (goDir*0.9 + wallNormal*0.1);
  5378 			wallPushVec.setLength2D(vars->maxWallJumpBurstSpeed);
  5379 
  5380 			position.stop();
  5381 			pushingOffWallEffect = 0.5;
  5382 			vel = wallPushVec;
  5383 			this->state.lockedToWall = false;
  5384 			skeletalSprite.stopAllAnimations();
  5385 			if (wallJumps > 0)
  5386 			{
  5387 				dsq->sound->playSfx("WallJump", 255, 0, 1000+wallJumps*100);
  5388 			}
  5389 			dsq->game->playBurstSound(pushingOffWallEffect>0);
  5390 			skeletalSprite.animate(getBurstAnimName(), 0);
  5391 			bursting = true;
  5392 			burst = 1.5;
  5393 			ripples = true;
  5394 
  5395 			startBurstCommon();
  5396 			/*
  5397 			if (core->afterEffectManager)
  5398 				core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.04,0.06,15,0.2f));
  5399 			*/
  5400 
  5401 
  5402 			/*
  5403 			float len = wallPushVec.getLength2D();
  5404 			goDir |= len;
  5405 			goDir.z = 0;
  5406 
  5407 			Vector test = goDir;
  5408 			test |= TILE_SIZE*2;
  5409 			if (dsq->game->isObstructed(TileVector(position + test)))
  5410 			{
  5411 			}
  5412 			else
  5413 				wallPushVec = goDir;
  5414 			*/
  5415 			/*
  5416 			wallPushVec = Vector((wallPushVec.x+goDir.x)/2, (wallPushVec.y+goDir.y)/2);
  5417 			wallPushVec |= len;
  5418 			*/
  5419 		}
  5420 		else
  5421 		{
  5422 			//wallPushVec |= 10;
  5423 		}
  5424 	}
  5425 }
  5426 
  5427 void Avatar::doDodgeInput(const std::string &action, int s)
  5428 {
  5429 	if (s)
  5430 	{
  5431 		if (tapped.empty())
  5432 		{
  5433 			tapped = action;
  5434 			state.tapTimer.start(0.25);
  5435 		}
  5436 		else if (tapped == action)
  5437 		{
  5438 			if (state.tapTimer.isActive())
  5439 				dodge(action);
  5440 			tapped = "";
  5441 			dodgeDelay = 1.0;
  5442 		}
  5443 		else
  5444 		{
  5445 			tapped = "";
  5446 		}
  5447 	}
  5448 }
  5449 
  5450 Vector Avatar::getKeyDir()
  5451 {
  5452 	Vector dir;
  5453 	if (isActing(ACTION_SWIMLEFT))
  5454 		dir += Vector(-1,0);
  5455 	if (isActing(ACTION_SWIMRIGHT))
  5456 		dir += Vector(1,0);
  5457 	if (isActing(ACTION_SWIMUP))
  5458 		dir += Vector(0,-1);
  5459 	if (isActing(ACTION_SWIMDOWN))
  5460 		dir += Vector(0,1);
  5461 
  5462 	if (dir.x != 0 && dir.y != 0)
  5463 		dir/=2;
  5464 
  5465 	return dir;
  5466 }
  5467 
  5468 Vector Avatar::getVectorToCursorFromScreenCentre()
  5469 {
  5470 	/*
  5471 	if (core->joystickEnabled)
  5472 	{
  5473 		Vector joy(core->joystate.lX-(65536/2), core->joystate.lY-(65536/2));
  5474 		float len = (joy.getLength2D() * 600) / 65536;
  5475 		joy.setLength2D(len);
  5476 		std::ostringstream os;
  5477 		os << "joy (" << joy.x << ", " << joy.y << ")";
  5478 		debugLog(os.str());
  5479 		return joy;
  5480 	}
  5481 	*/
  5482 	if (game->cameraOffBounds)
  5483 		return getVectorToCursor();
  5484 	else
  5485 	{
  5486 		if (dsq->inputMode == INPUT_KEYBOARD)
  5487 		{
  5488 			return getKeyDir() * 350;
  5489 		}
  5490 		if (dsq->inputMode == INPUT_JOYSTICK)
  5491 		{
  5492 			return (core->joystick.position * 350);
  5493 		}
  5494 		return (core->mouse.position+offset) - Vector(400,300);
  5495 	}
  5496 }
  5497 
  5498 Vector Avatar::getVectorToCursor(bool trueMouse)
  5499 {
  5500 	//return getVectorToCursorFromScreenCentre();
  5501 	Vector pos = dsq->getGameCursorPosition();
  5502 	
  5503 
  5504 	if (!trueMouse)
  5505 	{
  5506 		if (dsq->inputMode == INPUT_KEYBOARD)
  5507 		{
  5508 			pos = getKeyDir() * 350 + (position+offset);
  5509 		}
  5510 		if (dsq->inputMode == INPUT_JOYSTICK)
  5511 		{
  5512 			pos = core->joystick.position * 350 + (position+offset);
  5513 		}
  5514 	}
  5515 
  5516 	return pos - (position+offset);
  5517 	//return core->mouse.position - Vector(400,300);
  5518 }
  5519 
  5520 void Avatar::startWallCrawl()
  5521 {
  5522 	lastWallNormal = wallNormal;
  5523 	state.crawlingOnWall = true;
  5524 	skeletalSprite.transitionAnimate("crawl", 0.1, -1);
  5525 }
  5526 
  5527 void Avatar::stopWallCrawl()
  5528 {
  5529 	state.crawlingOnWall = false;
  5530 	state.lockedToWall = false;
  5531 	idle();
  5532 }
  5533 
  5534 void Avatar::action(int id, int state)
  5535 {
  5536 	if (id == ACTION_PRIMARY)	{ if (state) lmbd(); else lmbu(); }
  5537 	if (id == ACTION_SECONDARY) { if (state) rmbd(); else rmbu(); }
  5538 
  5539 	if (id == ACTION_PRIMARY && state)// !state
  5540 	{
  5541 		if (isMiniMapCursorOkay())
  5542 		{
  5543 			if (this->state.lockedToWall && !this->state.crawlingOnWall)
  5544 			{
  5545 				Vector test = getVectorToCursor();
  5546 				if (test.isLength2DIn(minMouse))
  5547 				{
  5548 					fallOffWall();
  5549 					// previously didn't fall off wall with mouse.... why?
  5550 					/*
  5551 					//fallOffWall();
  5552 					if (dsq->inputMode == INPUT_JOYSTICK || dsq->inputMode == INPUT_KEYBOARD)
  5553 						fallOffWall();
  5554 					*/
  5555 				}
  5556 				/*
  5557 				else if (test.isLength2DIn(maxMouse))
  5558 				{
  5559 					this->state.lockedToWall = false;
  5560 					idle();
  5561 				}
  5562 				*/
  5563 				else
  5564 				{
  5565 					//if (dsq->continuity.setFlag("lockedToWall", 1)
  5566 
  5567 					//!boneLock.entity && 
  5568 					if (boneLock.entity)
  5569 						wallNormal = boneLock.wallNormal;
  5570 
  5571 					if (isUnderWater())
  5572 					{
  5573 						test.normalize2D();
  5574 						float dott = wallNormal.dot2D(test);
  5575 						burst = 1;
  5576 						bursting = false;
  5577 						// normal is 90 degrees within/on the right side
  5578 						if (dott > 0)
  5579 						{
  5580 							startWallBurst(true);
  5581 						}
  5582 						else
  5583 						{
  5584 							startWallBurst(false);
  5585 						}
  5586 					}
  5587 					else
  5588 					{
  5589 						test.normalize2D();
  5590 						float dott = wallNormal.dot2D(test);
  5591 
  5592 						if (dott > -0.3)
  5593 						{
  5594 							burst = 1;
  5595 							bursting = false;
  5596 							startWallBurst(true);
  5597 						}
  5598 						else
  5599 						{
  5600 							// nothing
  5601 						}
  5602 					}
  5603 				}
  5604 
  5605 				/*
  5606 				else
  5607 				{
  5608 					wallNormal = getWallNormal(TileVector(position));
  5609 					Vector left = wallNormal.getPerpendicularLeft();
  5610 					Vector right = wallNormal.getPerpendicularRight();
  5611 					position += left*0.1;
  5612 				}
  5613 				*/
  5614 			}
  5615 			else
  5616 			{
  5617 				startBurst();
  5618 			}
  5619 		}
  5620 	}
  5621 	/*
  5622 	// song note keys
  5623 	else if (!action.empty() && action[0] == 's')
  5624 	{
  5625 		if (isSinging())
  5626 		{
  5627 			int count=0;
  5628 			std::istringstream is(action.substr(1, action.size()));
  5629 			is >> count;
  5630 
  5631 			count--;
  5632 			if (count >= 0 && count <= 7)
  5633 			{
  5634 				core->setMousePosition(songIcons[count]->position);
  5635 			}
  5636 		}
  5637 	}
  5638 	*/
  5639 	else if (id >= ACTION_SONGSLOT1 && id < ACTION_SONGSLOTEND)
  5640 	{
  5641 		if (canQuickSong())
  5642 		{
  5643 			int count = (id - ACTION_SONGSLOT1)+1;
  5644 
  5645 			bool cast = false;
  5646 
  5647 			if (dsq->continuity.form == FORM_SPIRIT)
  5648 			{
  5649 				revert();
  5650 				cast = true;
  5651 			}
  5652 			else
  5653 			{
  5654 				switch(count)
  5655 				{
  5656 				case 1:
  5657 				{
  5658 					if (dsq->continuity.form != FORM_NORMAL)
  5659 					{
  5660 						revert();
  5661 						cast = true;
  5662 					}
  5663 				}
  5664 				break;
  5665 				case 2:
  5666 					if (dsq->continuity.form != FORM_ENERGY)
  5667 					{
  5668 						dsq->continuity.castSong(SONG_ENERGYFORM);
  5669 						cast = true;
  5670 					}
  5671 				break;
  5672 				case 3:
  5673 					if (dsq->continuity.form != FORM_BEAST)
  5674 					{
  5675 						dsq->continuity.castSong(SONG_BEASTFORM);
  5676 						cast = true;
  5677 					}
  5678 				break;
  5679 				case 4:
  5680 					if (dsq->continuity.form != FORM_NATURE)
  5681 					{
  5682 						dsq->continuity.castSong(SONG_NATUREFORM);
  5683 						cast = true;
  5684 					}
  5685 				break;
  5686 				case 5:
  5687 					if (dsq->continuity.form != FORM_SUN)
  5688 					{
  5689 						dsq->continuity.castSong(SONG_SUNFORM);
  5690 						cast = true;
  5691 					}
  5692 				break;
  5693 				case 6:
  5694 					if (dsq->continuity.form != FORM_FISH)
  5695 					{
  5696 						dsq->continuity.castSong(SONG_FISHFORM);
  5697 						cast = true;
  5698 					}
  5699 				break;
  5700 				case 7:
  5701 					if (dsq->continuity.form != FORM_SPIRIT)
  5702 					{
  5703 						dsq->continuity.castSong(SONG_SPIRITFORM);
  5704 						cast = true;
  5705 					}
  5706 				break;
  5707 				case 8:
  5708 					if (dsq->continuity.form != FORM_DUAL)
  5709 					{
  5710 						dsq->continuity.castSong(SONG_DUALFORM);
  5711 						cast = true;
  5712 					}
  5713 				break;
  5714 				default:
  5715 					if (dsq->continuity.form == FORM_NORMAL)
  5716 					{
  5717 						switch(count)
  5718 						{
  5719 						case 9:
  5720 						{
  5721 							dsq->continuity.castSong(SONG_SHIELDAURA);
  5722 							cast = true;
  5723 						}
  5724 						break;
  5725 						case 10:
  5726 						{
  5727 							dsq->continuity.castSong(SONG_BIND);
  5728 							cast = true;
  5729 						}
  5730 						break;
  5731 						}
  5732 					}
  5733 				break;
  5734 				}
  5735 			}
  5736 
  5737 
  5738 			if (cast)
  5739 			{
  5740 				quickSongCastDelay = QUICK_SONG_CAST_DELAY;
  5741 			}
  5742 		}
  5743 	}
  5744 }
  5745 
  5746 void Avatar::doRangePull(float dt)
  5747 {
  5748 	int range = 4;
  5749 	Vector total;
  5750 
  5751 	Vector dest = position + vel*dt;
  5752 	TileVector t(dest);
  5753 	std::vector<Vector> vectors;
  5754 	for (int x = t.x-range; x <= t.x+range; x++)
  5755 	{
  5756 		for (int y = t.y-range; y <= t.y+range; y++)
  5757 		{
  5758 			TileVector tile(x,y);
  5759 			if (!(tile.x == t.x &&  tile.y == t.y) && !dsq->game->isObstructed(tile)
  5760 				&& !dsq->game->isObstructed(TileVector(tile.x+1, tile.y))
  5761 				&& !dsq->game->isObstructed(TileVector(tile.x, tile.y+1))
  5762 				&& !dsq->game->isObstructed(TileVector(tile.x-1, tile.y))
  5763 				&& !dsq->game->isObstructed(TileVector(tile.x, tile.y+1))
  5764 				)
  5765 			{
  5766 				vectors.push_back(tile.worldVector());
  5767 				/*
  5768 				Vector obs = tile.worldVector();
  5769 				Vector mov = position - obs;
  5770 
  5771 				int len = range*TILE_SIZE - mov.getLength2D();
  5772 				if (len < 0) len = 1;
  5773 				mov |= len;
  5774 				total += mov;
  5775 				*/
  5776 			}
  5777 		}
  5778 	}
  5779 
  5780 	unsigned int amount = 5;
  5781 	if (amount > vectors.size())
  5782 		amount = vectors.size();
  5783 	std::vector<int>smallestDists;
  5784 	smallestDists.resize(amount);
  5785 	int i = 0;
  5786 	for (i = 0; i < smallestDists.size(); i++)
  5787 	{
  5788 		smallestDists[i] = -1;
  5789 	}
  5790 	std::vector<Vector> closestVectors;
  5791 	closestVectors.resize(amount);
  5792 	for (i = 0; i < vectors.size(); i++)
  5793 	{
  5794 		Vector diff = dest - vectors[i];
  5795 		int dist = diff.getSquaredLength2D();
  5796 		for (int j = 0; j < smallestDists.size(); j++)
  5797 		{
  5798 			if (dist < smallestDists[j] || smallestDists[j] == -1)
  5799 			{
  5800 				for (int k = smallestDists.size()-1; k > j; k--)
  5801 				{
  5802 					smallestDists[k] = smallestDists[k-1];
  5803 					closestVectors[k] = closestVectors[k-1];
  5804 				}
  5805 				smallestDists[j] = dist;
  5806 				closestVectors[j] = vectors[i];
  5807 			}
  5808 		}
  5809 	}
  5810 
  5811 	for (i = 0; i < closestVectors.size(); i++)
  5812 	{
  5813 		Vector obs = closestVectors[i];
  5814 		if (obs.x == 0 && obs.y == 0) continue;
  5815 		Vector mov = obs - position;
  5816 
  5817 		int len = range*TILE_SIZE - mov.getLength2D();
  5818 		if (len < 0) len = 0;
  5819 		else
  5820 		{
  5821 			mov.setLength2D(len);
  5822 			total += mov;
  5823 		}
  5824 	}
  5825 
  5826 	if (total.x != 0 || total.y != 0)
  5827 	{
  5828 		float vlen = vel.getLength2D();
  5829 		//float len = (range*TILE_SIZE - avgDist)/range*TILE_SIZE;
  5830 		//if (len > 0)
  5831 		{
  5832 			if (bursting && swimming)
  5833 			{
  5834 				total.setLength2D(dt*4000);
  5835 			}
  5836 			else if (swimming)
  5837 			{
  5838 				//vel = Vector(0,0,0);
  5839 				//vel |= vlen;
  5840 				total.setLength2D(dt*1000);
  5841 			}
  5842 			else
  5843 			{
  5844 				total.setLength2D(dt*200);
  5845 			}
  5846 			vel += total;
  5847 		}
  5848 		/*
  5849 		if (vlen < 250)
  5850 		{
  5851 			total |= 200*dt;
  5852 		}
  5853 		else
  5854 		*/
  5855 		/*
  5856 		{
  5857 			total |= 500*dt;
  5858 		}
  5859 		*/
  5860 
  5861 	}
  5862 }
  5863 
  5864 void Avatar::doRangePush(float dt)
  5865 {
  5866 	// current not used
  5867 	/*
  5868 	if (vel.getSquaredLength2D() < sqr(1)) return;
  5869 	int range = 4;
  5870 	Vector total;
  5871 	TileVector t(position);
  5872 	std::vector<Vector> vectors;
  5873 	for (int x = t.x-range; x <= t.x+range; x++)
  5874 	{
  5875 		for (int y = t.y-range; y <= t.y+range; y++)
  5876 		{
  5877 			TileVector tile(x,y);
  5878 			if (dsq->game->isObstructed(tile))
  5879 			{
  5880 				vectors.push_back(tile.worldVector());
  5881 			}
  5882 		}
  5883 	}
  5884 
  5885 	int amount = 5;
  5886 	if (amount > vectors.size())
  5887 		amount = vectors.size();
  5888 	std::vector<int>smallestDists;
  5889 	smallestDists.resize(amount);
  5890 	for (int i = 0; i < smallestDists.size(); i++)
  5891 	{
  5892 		smallestDists[i] = -1;
  5893 	}
  5894 	std::vector<Vector> closestVectors;
  5895 	closestVectors.resize(amount);
  5896 	for (int i = 0; i < vectors.size(); i++)
  5897 	{
  5898 		Vector diff = position - vectors[i];
  5899 		int dist = diff.getSquaredLength2D();
  5900 		for (int j = 0; j < smallestDists.size(); j++)
  5901 		{
  5902 			if (dist < smallestDists[j] || smallestDists[j] == -1)
  5903 			{
  5904 				for (int k = smallestDists.size()-1; k > j; k--)
  5905 				{
  5906 					smallestDists[k] = smallestDists[k-1];
  5907 					closestVectors[k] = closestVectors[k-1];
  5908 				}
  5909 				smallestDists[j] = dist;
  5910 				closestVectors[j] = vectors[i];
  5911 			}
  5912 		}
  5913 	}
  5914 
  5915 	float tot=0;
  5916 	int c = 0;
  5917 	for (int i = 0; i < smallestDists.size(); i++)
  5918 	{
  5919 		if (smallestDists[i] != -1)
  5920 		{
  5921 			tot += smallestDists[i];
  5922 			c++;
  5923 		}
  5924 	}
  5925 	float avgDist = range*TILE_SIZE;
  5926 	if (c > 0)
  5927 	{
  5928 		avgDist = tot / c;
  5929 	}
  5930 
  5931 
  5932 
  5933 	for (int i = 0; i < closestVectors.size(); i++)
  5934 	{
  5935 		Vector obs = closestVectors[i];
  5936 		if (obs.x == 0 && obs.y == 0) continue;
  5937 		Vector mov = position - obs;
  5938 
  5939 		int len = range*range*TILE_SIZE - mov.getLength2D();
  5940 		if (len < 0) len = 0;
  5941 		else
  5942 		{
  5943 			mov |= len;
  5944 			total += mov;
  5945 		}
  5946 	}
  5947 
  5948 	if (total.x != 0 || total.y != 0)
  5949 	{
  5950 		float vlen = vel.getLength2D();
  5951 		//float len = (range*TILE_SIZE - avgDist)/range*TILE_SIZE;
  5952 		//if (len > 0)
  5953 		{
  5954 			float totLen = 0;
  5955 			if (swimming)
  5956 			{
  5957 				totLen = dt*600;
  5958 			}
  5959 			else
  5960 			{
  5961 				totLen = dt*50;
  5962 			}
  5963 			total |= totLen;
  5964 
  5965 			Vector perp = vel;
  5966 			Vector n = vel;
  5967 			n.normalize2D();
  5968 			float d = total.dot2D(n);
  5969 			perp |= d;
  5970 			total -= perp;
  5971 			total |= totLen;
  5972 			lastPush = total;
  5973 			vel += total;
  5974 		}
  5975 	}
  5976 	*/
  5977 
  5978 }
  5979 
  5980 void Avatar::render()
  5981 {
  5982 
  5983 	if (dsq->continuity.form == FORM_SPIRIT && !skeletalSprite.getParent())
  5984 	{
  5985 		skeletalSprite.position = bodyPosition+bodyOffset;
  5986 		skeletalSprite.color = Vector(0.2, 0.3, 0.6);
  5987 		skeletalSprite.render();
  5988 		skeletalSprite.color = Vector(1,1,1);
  5989 	}
  5990 
  5991 	Entity::render();
  5992 
  5993 	if (ropeState != 0)
  5994 	{
  5995 #ifdef BBGE_BUILD_OPENGL
  5996 		glPushMatrix();
  5997 			glColor4f(1.0f,0.0f,0.0f,1.0f);
  5998 			glBegin(GL_LINES);
  5999 				glVertex3f(position.x, position.y, 0);
  6000 				glVertex3f(ropePos.x, ropePos.y, 0);
  6001 			glEnd();
  6002 		glPopMatrix();
  6003 #endif
  6004 	}
  6005 
  6006 	/*
  6007 	if (activeAura == AURA_SHIELD)
  6008 	{
  6009 		glPushMatrix();
  6010 		glColor4f(0,0.5,1,1);
  6011 		glTranslatef(shieldPosition.x, shieldPosition.y, 0);
  6012 		drawCircle(AURA_SHIELD_RADIUS, 8);
  6013 		glPopMatrix();
  6014 	}
  6015 	*/
  6016 
  6017 }
  6018 
  6019 void Avatar::onRender()
  6020 {
  6021 	Entity::onRender();
  6022 
  6023 	/*
  6024 	// HACK
  6025 	if (RenderObject::renderPaths)
  6026 	{
  6027 		glPushMatrix();
  6028 			glLoadIdentity();
  6029 			glEnable(GL_BLEND);
  6030 			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  6031 			glColor4f(1, 0, 0, 0.5);
  6032 			glTranslatef(400, 300, 0);
  6033 			drawCircle(64);
  6034 		glPopMatrix();
  6035 	}
  6036 	*/
  6037 
  6038 	//dsq->print(20, 600-100, "Naija: Hello there. My name is fred.");
  6039 	/*
  6040 	std::ostringstream os;
  6041 	os << lastPush.x << ", " << lastPush.y;
  6042 	debugLog(os.str());
  6043 	*/
  6044 	/*
  6045 	glPopMatrix();
  6046 	glPushMatrix();
  6047 	glTranslatef(position.x, position.y, position.z);
  6048 	//glRotatef(0, 0, 1, -rotation.z);
  6049 	glDisable(GL_BLEND);
  6050 	glPointSize(12);
  6051 	glDisable(GL_LIGHTING);
  6052 	glColor3f(1,0,0);
  6053 	glBegin(GL_LINES);
  6054 		//glColor3f(1, 0, 0);
  6055 		glVertex3f(0,0,0);
  6056 		//glColor3f(1, 0, 0);
  6057 		glVertex3f(lastPush.x*50, lastPush.y*50, 0);
  6058 	glEnd();
  6059 	glPopMatrix();
  6060 	*/
  6061 }
  6062 
  6063 int Avatar::getBeamWidth()
  6064 {
  6065 	const int MAX_BEAM_LEN = 50;
  6066 	Vector mov = dsq->getGameCursorPosition() - this->position;
  6067 	mov.setLength2D(1);
  6068 	TileVector t(position);
  6069 	Vector tile(t.x, t.y);
  6070 	int c = 0;
  6071 	while (c < MAX_BEAM_LEN)
  6072 	{
  6073 		bool hit = false;
  6074 		tile += mov;
  6075 		TileVector t;
  6076 		t.x = int(tile.x);
  6077 		t.y = int(tile.y);
  6078 		if (dsq->game->isObstructed(t))
  6079 		{
  6080 			hit = true;
  6081 		}
  6082 
  6083 		FOR_ENTITIES(i)
  6084 		{
  6085 			Entity *e = *i;
  6086 			if (e != this)
  6087 			{
  6088 				TileVector et(e->position);
  6089 				Vector t1(et.x, et.y);
  6090 				Vector t2(tile.x, tile.y);
  6091 				Vector diff = t1-t2;
  6092 				if (diff.getSquaredLength2D() <= 1)
  6093 				{
  6094 					// HACK: replace damage function
  6095 					//e->damage(1, 0, this);
  6096 					hit = true;
  6097 				}
  6098 			}
  6099 		}
  6100 		if (hit)
  6101 			break;
  6102 		c++;
  6103 	}
  6104 	return c * TILE_SIZE;
  6105 }
  6106 
  6107 void Avatar::onGetEXP(unsigned int exp)
  6108 {
  6109 	dsq->continuity.exp += exp;
  6110 }
  6111 
  6112 void Avatar::onEnterState(int action)
  6113 {
  6114 	Entity::onEnterState(action);
  6115 	if (action == STATE_TRANSFORM)
  6116 	{
  6117 		animator.stop();
  6118 		frame = 0;
  6119 		animate(anim_fish);
  6120 	}
  6121 	else if (action == STATE_PUSH)
  6122 	{
  6123 		state.lockedToWall = false;
  6124 		state.crawlingOnWall = false;
  6125 		Animation *a = skeletalSprite.getCurrentAnimation();
  6126 		if (!a || (a && a->name != "pushed"))
  6127 			skeletalSprite.animate("pushed", 0);
  6128 	}
  6129 	else if (action == STATE_EATING)
  6130 	{
  6131 		/*
  6132 		// the problems with this:
  6133 		// 1. what happens out of water?
  6134 		// 2. is the delay too long?
  6135 		// could just play a sound and then spawn some kind of particle effect
  6136 		disableInput();
  6137 		idle();
  6138 		vel=vel2=Vector(0,0);
  6139 		skeletalSprite.animate("eat", 0, ANIM_OVERRIDE);
  6140 		core->main(1);
  6141 		enableInput();
  6142 		*/
  6143 	}
  6144 }
  6145 
  6146 void Avatar::onExitState(int action)
  6147 {
  6148 	Entity::onExitState(action);
  6149 	if (action == STATE_TRANSFORM)
  6150 	{
  6151 		setState(STATE_IDLE);
  6152 	}
  6153 	else if (action == STATE_PUSH)
  6154 	{
  6155 		skeletalSprite.transitionAnimate("spin", 0.1);
  6156 		/*
  6157 		rotation.z = rotation.z+360;
  6158 		rotation.interpolateTo(Vector(0,0,rotation.z-360-90), 0.5);
  6159 		*/
  6160 	}
  6161 }
  6162 
  6163 void Avatar::splash(bool down)
  6164 {
  6165 	if (down)
  6166 	{
  6167 		int freq = 1000;
  6168 		if (rolling)
  6169 			freq = 900;
  6170 		sound("splash-into", freq);
  6171 		//dsq->postProcessingFx.disable(FXT_RADIALBLUR);
  6172 		if (_isUnderWater && core->afterEffectManager)
  6173 			core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
  6174 		dsq->rumble(0.7, 0.7, 0.2);
  6175 		plungeEmitter.start();
  6176 	}
  6177 	else
  6178 	{
  6179 		sound("splash-outof");
  6180 		/*
  6181 		dsq->postProcessingFx.enable(FXT_RADIALBLUR);
  6182 		dsq->postProcessingFx.radialBlurColor = Vector(1,1,1);
  6183 		dsq->postProcessingFx.intensity = 0.1;
  6184 		*/
  6185 	}
  6186 	// make a splash effect @ current position
  6187 
  6188 
  6189 	float a = 0;
  6190 
  6191 	if (waterBubble || lastJumpOutFromWaterBubble)
  6192 	{
  6193 		lastJumpOutFromWaterBubble = false;
  6194 
  6195 		if (waterBubble)
  6196 		{
  6197 			Vector diff = position - waterBubble->nodes[0].position;
  6198 			a = MathFunctions::getAngleToVector(diff, 180);
  6199 		}
  6200 		else if (lastWaterBubble)
  6201 		{
  6202 			Vector diff = position - lastWaterBubble->nodes[0].position;
  6203 			a = MathFunctions::getAngleToVector(diff, 0);
  6204 		}
  6205 		else
  6206 			a = MathFunctions::getAngleToVector(vel+vel2, 0);
  6207 	}
  6208 
  6209 	dsq->spawnParticleEffect("Splash", position, a);
  6210 
  6211 
  6212 	//Vector(position.x, dsq->game->waterLevel.x));
  6213 	/*
  6214 	Quad *splash = new Quad;
  6215 	splash->setTexture("splash");
  6216 	splash->position = this->position;
  6217 	splash->position.y = dsq->game->waterLevel-splash->width.x/2;
  6218 	float t = 0.5;
  6219 	splash->alpha.path.addPathNode(0, 0);
  6220 	splash->alpha.path.addPathNode(1, 0.25);
  6221 	splash->alpha.path.addPathNode(0, 1);
  6222 	splash->alpha.startPath(t);
  6223 	splash->scale = Vector(1.5, 0.9);
  6224 	splash->scale.interpolateTo(Vector(0.5,1.2),t);
  6225 	splash->setLife(1);
  6226 	splash->setDecayRate(0.9);
  6227 	core->getTopStateData()->addRenderObject(splash, LR_PARTICLES);
  6228 	*/
  6229 }
  6230 
  6231 void Avatar::clampVelocity()
  6232 {
  6233 	/*
  6234 	std::ostringstream os;
  6235 	os << "currentMaxSpeed: " << currentMaxSpeed;
  6236 	debugLog(os.str());
  6237 	*/
  6238 	float useSpeedMult = dsq->continuity.speedMult;
  6239 	bool inCurrent = isInCurrent();
  6240 	bool withCurrent = false;
  6241 
  6242 	if (inCurrent)
  6243 	{
  6244 		// if vel2 and vel are pointing the same way
  6245 		if (vel2.dot2D(vel) > 0)
  6246 		{
  6247 			withCurrent = true;
  6248 		}
  6249 	}
  6250 
  6251 
  6252 
  6253 
  6254 
  6255 	if (dsq->continuity.form == FORM_BEAST)
  6256 	{
  6257 		currentMaxSpeed *= MULT_MAXSPEED_BEASTFORM;
  6258 	}
  6259 
  6260 	if (!inCurrent || (inCurrent && withCurrent))
  6261 	{
  6262 		if (dsq->continuity.form == FORM_FISH)
  6263 		{
  6264 			currentMaxSpeed *= MULT_MAXSPEED_FISHFORM;
  6265 		}
  6266 	}
  6267 	else
  6268 	{
  6269 		useSpeedMult = 1;
  6270 	}
  6271 
  6272 	if (currentState == STATE_PUSH)
  6273 	{
  6274 		currentMaxSpeed = pushMaxSpeed;
  6275 	}
  6276 
  6277 
  6278 	setMaxSpeed(currentMaxSpeed * useSpeedMult);
  6279 	float cheatLen = vel.getSquaredLength2D();
  6280 	vel.capLength2D(getMaxSpeed());
  6281 	/*
  6282 	if (cheatLen > sqr(getMaxSpeed()))
  6283 		vel.setLength2D(getMaxSpeed());
  6284 	*/
  6285 }
  6286 
  6287 void Avatar::activateAura(AuraType aura)
  6288 {
  6289 	activeAura = aura;
  6290 	auraTimer = 30;
  6291 	if (aura == AURA_SHIELD)
  6292 	{
  6293 		shieldPoints = maxShieldPoints;
  6294 		auraEmitter.load("AuraShield");
  6295 		auraEmitter.start();
  6296 		if (dsq->loops.shield == BBGE_AUDIO_NOCHANNEL)
  6297 		{
  6298 			PlaySfx play;
  6299 			play.name = "Shield-Loop";
  6300 			play.fade = SFT_IN;
  6301 			play.time = 1;
  6302 			play.loops = -1;
  6303 			dsq->loops.shield = core->sound->playSfx(play);
  6304 		}
  6305 	}
  6306 }
  6307 
  6308 void Avatar::updateAura(float dt)
  6309 {
  6310 	if (auraTimer > 0 && dsq->continuity.form != FORM_SPIRIT)
  6311 	{
  6312 		switch(activeAura)
  6313 		{
  6314 		case AURA_SHIELD:
  6315 		{
  6316 			//shieldPosition = position + Vector(cos(auraTimer*4)*100, sin(auraTimer*4)*100);
  6317 			shieldPosition = position;
  6318 			/*
  6319 			float a = ((rotation.z)*PI)/180.0 + PI*0.5;
  6320 			shieldPosition = position + Vector(cosf(a)*100, sinf(a)*100);
  6321 			*/
  6322 			for (Shot::Shots::iterator i = Shot::shots.begin(); i != Shot::shots.end(); ++i)
  6323 			{
  6324 				//&& (*i)->life > 0.2
  6325 				if ((*i) && dsq->game->isDamageTypeEnemy((*i)->getDamageType()) && (*i)->firer != this
  6326 					&& (!(*i)->shotData || !(*i)->shotData->ignoreShield))
  6327 				{
  6328 
  6329 					Vector diff = (*i)->position - shieldPosition;
  6330 					if (diff.getSquaredLength2D() < sqr(AURA_SHIELD_RADIUS))
  6331 					{
  6332 						shieldPoints -= (*i)->getDamage();
  6333 						auraHitEmitter.start();
  6334 						dsq->spawnParticleEffect("ReflectShot", (*i)->position);
  6335 						core->sound->playSfx("Shield-Hit");
  6336 						(*i)->position += diff;
  6337 						//(*i)->target = 0;
  6338 						diff.setLength2D((*i)->maxSpeed);
  6339 						(*i)->velocity = diff;
  6340 						(*i)->reflectFromEntity(this);
  6341 					}
  6342 				}
  6343 			}
  6344 		}
  6345 		break;
  6346 		}
  6347 
  6348 		auraTimer -= dt;
  6349 		if (auraTimer < 5 || shieldPoints < 2)
  6350 		{
  6351 			if (auraEmitter.name == "AURASHIELD")
  6352 			{
  6353 				auraEmitter.stop();
  6354 				auraEmitter.load("AuraShieldLow");
  6355 				auraEmitter.start();
  6356 			}
  6357 		}
  6358 
  6359 		if (auraTimer < 0 || shieldPoints < 0)
  6360 		{
  6361 			stopAura();
  6362 		}
  6363 	}
  6364 }
  6365 
  6366 void Avatar::stopAura()
  6367 {
  6368 	auraTimer = 0;
  6369 	activeAura = AURA_NONE;
  6370 	auraEmitter.stop();
  6371 	if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
  6372 	{
  6373 		core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
  6374 		dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
  6375 	}
  6376 }
  6377 
  6378 void Avatar::setHeadTexture(const std::string &name, float time)
  6379 {
  6380 	if (!bone_head) return;
  6381 	if (dsq->continuity.form == FORM_NORMAL /*&& dsq->continuity.costume.empty()*/)
  6382 	{
  6383 		if (!name.empty() && (nocasecmp(lastHeadTexture, "singing")==0)) return;
  6384 	
  6385 		lastHeadTexture = name;
  6386 		stringToUpper(lastHeadTexture);
  6387 		std::string t = "Naija/";
  6388 
  6389 		if (!dsq->continuity.costume.empty())
  6390 		{
  6391 			if (nocasecmp(dsq->continuity.costume, "end")==0)
  6392 				t += "Naija2";
  6393 			else
  6394 				t += dsq->continuity.costume;
  6395 		}
  6396 		else if (dsq->continuity.form == FORM_BEAST)
  6397 		{
  6398 			t += "Beast";
  6399 		}
  6400 		else
  6401 		{
  6402 			t += "Naija2";
  6403 		}
  6404 		t += "-Head";
  6405 		if (!name.empty())
  6406 		{
  6407 			t += "-" + name;
  6408 		}
  6409 		bone_head->setTexture(t);
  6410 
  6411 		headTextureTimer = time;
  6412 	}
  6413 }
  6414 
  6415 void Avatar::chargeVisualEffect(const std::string &tex)
  6416 {
  6417 	float time = 0.4;
  6418 	Quad *chargeEffect = new Quad;
  6419 	chargeEffect->setBlendType(BLEND_ADD);
  6420 	chargeEffect->alpha.path.addPathNode(0, 0);
  6421 	chargeEffect->alpha.path.addPathNode(0.6, 0.1);
  6422 	chargeEffect->alpha.path.addPathNode(0.6, 0.9);
  6423 	chargeEffect->alpha.path.addPathNode(0, 1.0);
  6424 	chargeEffect->alpha.startPath(time);
  6425 	chargeEffect->setTexture(tex);
  6426 	//chargeEffect->positionSnapTo = &this->position;
  6427 	chargeEffect->position = this->position;
  6428 	chargeEffect->setPositionSnapTo(&position);
  6429 	chargeEffect->setLife(1);
  6430 	chargeEffect->setDecayRate(1.0/time);
  6431 	chargeEffect->scale = Vector(0.1, 0.1);
  6432 	chargeEffect->scale.interpolateTo(Vector(1,1),time);
  6433 	//chargeEffect->rotation.interpolateTo(Vector(0,0,360), time);
  6434 	dsq->game->addRenderObject(chargeEffect, LR_PARTICLES);
  6435 }
  6436 
  6437 void Avatar::updateFormVisualEffects(float dt)
  6438 {
  6439 	switch (dsq->continuity.form)
  6440 	{
  6441 	case FORM_ENERGY:
  6442 	{
  6443 		Vector hairDir(96, -96);
  6444 		if (this->isfh())
  6445 		{
  6446 			hairDir.x = -hairDir.x;
  6447 		}
  6448 	}
  6449 	break;
  6450 	case FORM_SUN:
  6451 	{
  6452 		lightFormGlow->position = this->position;
  6453 		lightFormGlowCone->position = this->position;
  6454 
  6455 		float angle=0;
  6456 		MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), getVectorToCursorFromScreenCentre(), angle);
  6457 		angle = 180-(360-angle);
  6458 
  6459 		//lightFormGlowCone->rotation.interpolateTo(Vector(0,0,angle), 0.1);
  6460 		lightFormGlowCone->rotation = Vector(0,0,angle);
  6461 
  6462 		static float lfgTimer = 0;
  6463 		lfgTimer += dt;
  6464 		if (lfgTimer > 0.5)
  6465 		{
  6466 			//debugLog("lightFormGlow to front");
  6467 			lightFormGlow->moveToFront();
  6468 			lightFormGlowCone->moveToFront();
  6469 			lfgTimer = 0;
  6470 		}
  6471 
  6472 		if (this->isInDarkness())
  6473 		{
  6474 			lightFormGlowCone->alphaMod = 1;
  6475 			lightFormGlow->alphaMod = 1;
  6476 		}
  6477 		else
  6478 		{
  6479 			lightFormGlow->alphaMod = 0;
  6480 			lightFormGlowCone->alphaMod = 0;
  6481 		}
  6482 	}
  6483 	break;
  6484 	case FORM_SPIRIT:
  6485 		skeletalSprite.update(dt);
  6486 		skeletalSprite.position = bodyPosition;
  6487 	break;
  6488 	}
  6489 }
  6490 
  6491 void Avatar::stopBurst()
  6492 {
  6493 	burst = 0;
  6494 	//burstDelay = BURST_DELAY;
  6495 	burstDelay = 0;
  6496 	bursting = false;
  6497 	animatedBurst = false;
  6498 	wakeEmitter.stop();
  6499 	ripples = false;
  6500 
  6501 	biteLeftEmitter.stop();
  6502 	biteRightEmitter.stop();
  6503 //	lastWallJumpPos = Vector(0,0,0);
  6504 }
  6505 
  6506 int Avatar::getCursorQuadrant()
  6507 {
  6508 	//Vector diff = getVectorToCursorFromScreenCentre();
  6509 	Vector diff = getVectorToCursor();
  6510 	if (diff.isLength2DIn(40))
  6511 	{
  6512 		stopRoll();
  6513 		return -999;
  6514 	}
  6515 	//diff = getForward();
  6516 	float angle = atanf(diff.y / diff.x);
  6517 	//float angle = rotation.z;
  6518 
  6519 	/*
  6520 	std::ostringstream os;
  6521 	os << "angle: " << angle;
  6522 	debugLog(os.str());
  6523 	*/
  6524 
  6525 
  6526 	int quad = 0;
  6527 	if (angle > -1.6 && angle <= 0 && diff.x > 0)
  6528 		quad = 1;
  6529 	else if (angle > -1.6 && angle <= 0 && diff.x < 0 && diff.y > 0)
  6530 		quad = 3;
  6531 	else if (angle < 1.6 && angle >= 0 && diff.y < 0)
  6532 		quad = 4;
  6533 	else
  6534 		quad = 2;
  6535 
  6536 	return quad;
  6537 }
  6538 
  6539 int Avatar::getQuadrantDirection(int lastQuad, int quad)
  6540 {
  6541 	int diff = quad - lastQuad;
  6542 	//if (lastQuad==0) return 0;
  6543 	if ((lastQuad==4 && quad == 1))
  6544 	{
  6545 		diff = 1;
  6546 	}
  6547 	if (lastQuad==1 && quad==4)
  6548 	{
  6549 		diff = -1;
  6550 	}
  6551 	if (abs(diff) != 1)
  6552 		diff = 0;
  6553 	return diff;
  6554 }
  6555 
  6556 void Avatar::startRoll(int dir)
  6557 {
  6558 	if (!rolling && !state.backFlip)
  6559 	{
  6560 		if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
  6561 		{
  6562 			rollRightEmitter.load("EnergyRollRight");
  6563 			rollLeftEmitter.load("EnergyRollLeft");
  6564 		}
  6565 		else
  6566 		{
  6567 			rollRightEmitter.load("RollRight");
  6568 			rollLeftEmitter.load("RollLeft");
  6569 		}
  6570 	}
  6571 
  6572 	Animation *a = skeletalSprite.getCurrentAnimation();
  6573 	//debugLog("start roll!");
  6574 	if (!a || a->name != getRollAnimName())
  6575 	{
  6576 		skeletalSprite.transitionAnimate(getRollAnimName(), 0.2, -1);
  6577 	}
  6578 	/*
  6579 	rollRightEmitter.load("RollRight");
  6580 	rollLeftEmitter.load("RollLeft");
  6581 
  6582 	rollRightEmitter.start();
  6583 	rollLeftEmitter.start();
  6584 	*/
  6585 	rollRightEmitter.stop();
  6586 	rollLeftEmitter.stop();
  6587 
  6588 	if (_isUnderWater)
  6589 	{
  6590 		if (dir >= 1)
  6591 			rollRightEmitter.start();
  6592 
  6593 		if (dir <= -1)
  6594 			rollLeftEmitter.start();
  6595 	}
  6596 
  6597 	//dsq->playVisualEffect(VFX_RIPPLE, Vector());
  6598 	rolling = true;
  6599 
  6600 	if (dsq->loops.roll == BBGE_AUDIO_NOCHANNEL && _isUnderWater)
  6601 	{
  6602 		PlaySfx play;
  6603 		play.name = "RollLoop";
  6604 		play.fade = SFT_IN;
  6605 		play.time = 1;
  6606 		play.vol = 1;
  6607 		play.loops = -1;
  6608 		dsq->loops.roll = core->sound->playSfx(play);
  6609 	}
  6610 	else if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL && !_isUnderWater)
  6611 	{
  6612 		core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 0.5);
  6613 	}
  6614 	/*
  6615 	//HACK: make this dt based
  6616 	static int rollBits = 0;
  6617 	rollBits = rollBits + 1;
  6618 	if (rollBits > 6)
  6619 	{
  6620 		if (_isUnderWater)
  6621 			core->sound->playSfx("Roll2");
  6622 		rollBits = 0;
  6623 	}
  6624 	*/
  6625 	rollDir = dir;
  6626 
  6627 	/*
  6628 	if (core->afterEffectManager)
  6629 		core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.04,0.06,15,0.2f));
  6630 	*/
  6631 	if (_isUnderWater && core->afterEffectManager)
  6632 		core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.08,0.05,22,0.2f, 1.2));
  6633 
  6634 
  6635 	//rollDelay = 0.3;
  6636 }
  6637 
  6638 void Avatar::stopRoll()
  6639 {
  6640 	rolling = false;
  6641 	lastQuadDir = lastQuad = 0;
  6642 	rollDelay = 0;
  6643 	rollDidOne = 0;
  6644 	rollLeftEmitter.stop();
  6645 	rollRightEmitter.stop();
  6646 	state.rollTimer = 0;
  6647 
  6648 	if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL)
  6649 	{
  6650 		core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 1);
  6651 		dsq->loops.roll = BBGE_AUDIO_NOCHANNEL;
  6652 	}
  6653 }
  6654 
  6655 
  6656 void Avatar::stopWallJump()
  6657 {
  6658 	wallBurstTimer = 0;
  6659 	wallJumps = 0;
  6660 	lastWallJumpPos = Vector(0,0,0);
  6661 }
  6662 
  6663 void Avatar::updateWallJump(float dt)
  6664 {
  6665 	if (wallBurstTimer > 0)
  6666 	{
  6667 		wallBurstTimer -= dt;
  6668 		if (wallBurstTimer < 0)
  6669 		{
  6670 			// wall jump failed!
  6671 			stopWallJump();
  6672 		}
  6673 	}
  6674 }
  6675 
  6676 void Avatar::updateRoll(float dt)
  6677 {
  6678 	if (!inputEnabled || dsq->continuity.getWorldType() == WT_SPIRIT)
  6679 	{
  6680 		if (rolling)
  6681 			stopRoll();
  6682 		return;
  6683 	}
  6684 	if (state.lockedToWall || isSinging()) return;
  6685 
  6686 	if (rollDelay > 0)
  6687 	{
  6688 		/*
  6689 		std::ostringstream os;
  6690 		os << "rollDelay: " << rollDelay;
  6691 		debugLog(os.str());
  6692 		*/
  6693 		rollDelay -= dt;
  6694 		if (rollDelay <= 0)
  6695 		{
  6696 			// stop the animation
  6697 			stopRoll();
  6698 		}
  6699 	}
  6700 	
  6701 	if (!_isUnderWater && isActing(ACTION_ROLL))
  6702 	{
  6703 		stopRoll();
  6704 	}
  6705 
  6706 	if (!core->mouse.buttons.left && dsq->inputMode == INPUT_MOUSE && !isActing(ACTION_ROLL))
  6707 	{
  6708 		if (rolling)
  6709 			stopRoll();
  6710 		return;
  6711 	}
  6712 
  6713 	if (rolling)
  6714 	{
  6715 		/*
  6716 		for (int i = 0; i < dsq->entities.size(); i++)
  6717 		{
  6718 			Entity *e = dsq->entities[i];
  6719 			if (e->getEntityType() == ET_ENEMY && (e->position - this->position).isLength2DIn(350))
  6720 			{
  6721 				//e->move(dt, 500, 1, this);
  6722 				Vector diff = (position - e->position);
  6723 				diff.setLength2D(1000*dt);
  6724 				e->vel2 += diff;
  6725 			}
  6726 		}
  6727 		*/
  6728 		if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
  6729 		{
  6730 			FOR_ENTITIES(i)
  6731 			{
  6732 				Entity *e = *i;
  6733 				if (e->getEntityType() == ET_ENEMY && (e->position - this->position).isLength2DIn(256) && e->isDamageTarget(DT_AVATAR_ENERGYROLL))
  6734 				{
  6735 					DamageData d;
  6736 					d.damage = dt*15;
  6737 					d.damageType = DT_AVATAR_ENERGYROLL;
  6738 					d.attacker = this;
  6739 					e->damage(d);
  6740 				}
  6741 			}
  6742 		}
  6743 
  6744 		state.rollTimer += dt;
  6745 		if (state.rollTimer > 0.55)
  6746 		{
  6747 			state.rollTimer = 0;
  6748 			if (dsq->continuity.form == FORM_DUAL)
  6749 			{
  6750 				switchDualFormMode();
  6751 				state.rollTimer = -1;
  6752 			}
  6753 		}
  6754 
  6755 		/*
  6756 		//HACK: -ish, fixes low frame rate roll stuck problem?
  6757 		// nope
  6758 		if (rollDelay == 0)
  6759 		{
  6760 			rollDelay = 0.01;
  6761 		}
  6762 		*/
  6763 		// NOTE: does this fix the roll problem?
  6764 		if (rollDelay <= 0)
  6765 			stopRoll();
  6766 	}
  6767 	
  6768 	if (isActing(ACTION_ROLL))
  6769 	{
  6770 		if (_isUnderWater)
  6771 		{
  6772 			if (rollDelay < 0.5)
  6773 			{
  6774 				startRoll(isfh()?1:-1);
  6775 			}
  6776 			float amt = dt * 1000;
  6777 			if (isfh())
  6778 			{
  6779 				rotation.z += amt;
  6780 			}
  6781 			else
  6782 			{
  6783 				rotation.z -= amt;
  6784 			}
  6785 			rotation.capRotZ360();
  6786 			
  6787 			rollDelay = 1.0;
  6788 		}
  6789 	}
  6790 	else
  6791 	{
  6792 		int quad = getCursorQuadrant();
  6793 		if (lastQuad != quad)
  6794 		{
  6795 			int quadDir = 0;
  6796 			if (lastQuad != 0)
  6797 			{
  6798 				quadDir = getQuadrantDirection(lastQuad, quad);
  6799 				if (quadDir != 0 && lastQuadDir == quadDir && rollDelay > 0)
  6800 				{
  6801 					if (rolling)
  6802 					{
  6803 						startRoll(quadDir);
  6804 					}
  6805 					else
  6806 					{
  6807 						if (rollDidOne==1)
  6808 							rollDidOne = 2;
  6809 						else if (rollDidOne == 2)
  6810 							startRoll(quadDir);
  6811 						else
  6812 							rollDidOne = 1;
  6813 					}
  6814 				}
  6815 			}
  6816 			/*
  6817 			std::ostringstream os;
  6818 			os << "quad: " << quad << " lastQuad: " << lastQuad << " lastQuadDir: " << lastQuadDir;
  6819 			debugLog(os.str());
  6820 			*/
  6821 
  6822 
  6823 			/*
  6824 			if (lastQuad != 0)
  6825 			{
  6826 				lastQuadDir = quadDir;
  6827 			}
  6828 			*/
  6829 
  6830 			lastQuadDir = quadDir;
  6831 
  6832 			lastQuad = quad;
  6833 
  6834 			rollDelay = 0.2;
  6835 		}
  6836 	}
  6837 }
  6838 
  6839 int Avatar::getStopDistance()
  6840 {
  6841 	return STOP_DISTANCE;
  6842 }
  6843 
  6844 int Avatar::getBurstDistance()
  6845 {
  6846 	return BURST_DISTANCE;
  6847 }
  6848 
  6849 void Avatar::updateTummy(float dt)
  6850 {
  6851 	if (dsq->continuity.form == FORM_BEAST)
  6852 	{
  6853 		//dsq->shakeCamera(5, 0.1);
  6854 
  6855 		/*
  6856 		if (inTummy > 0)
  6857 		{
  6858 			tummyTimer += dt;
  6859 			if (tummyTimer > TUMMY_TIME)
  6860 			{
  6861 				//core->sound->playSfx("Digest");
  6862 				//heal(inTummy);
  6863 				inTummy = 0;
  6864 			}
  6865 		}
  6866 		*/
  6867 
  6868 	}
  6869 }
  6870 
  6871 void Avatar::setWasUnderWater()
  6872 {
  6873 	state.wasUnderWater = isUnderWater();
  6874 }
  6875 
  6876 bool Avatar::canActivateStuff()
  6877 {
  6878 	return dsq->continuity.form != FORM_SPIRIT;
  6879 }
  6880 
  6881 bool Avatar::canQuickSong()
  6882 {
  6883 	return !isSinging() && !isEntityDead() && isInputEnabled() && quickSongCastDelay <= 0;
  6884 }
  6885 
  6886 void Avatar::updateJoystick(float dt)
  6887 {
  6888 	if (canQuickSong())
  6889 	{
  6890 
  6891 		if (core->joystick.dpadUp)
  6892 		{
  6893 			if (dsq->continuity.hasSong(SONG_ENERGYFORM) && dsq->continuity.form != FORM_ENERGY)
  6894 			{
  6895 				quickSongCastDelay = QUICK_SONG_CAST_DELAY;
  6896 				dsq->continuity.castSong(SONG_ENERGYFORM);
  6897 			}
  6898 		}
  6899 		else if (core->joystick.dpadDown && dsq->continuity.hasSong(SONG_BEASTFORM) && dsq->continuity.form != FORM_BEAST)
  6900 		{
  6901 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
  6902 			dsq->continuity.castSong(SONG_BEASTFORM);
  6903 		}
  6904 		else if (core->joystick.dpadLeft && dsq->continuity.hasSong(SONG_SUNFORM) && dsq->continuity.form != FORM_SUN)
  6905 		{
  6906 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
  6907 			dsq->continuity.castSong(SONG_SUNFORM);
  6908 		}
  6909 		else if (core->joystick.dpadRight && dsq->continuity.hasSong(SONG_NATUREFORM) && dsq->continuity.form != FORM_NATURE)
  6910 		{
  6911 			quickSongCastDelay = QUICK_SONG_CAST_DELAY;
  6912 			dsq->continuity.castSong(SONG_NATUREFORM);
  6913 		}
  6914 
  6915 	}
  6916 }
  6917 
  6918 void Avatar::applyRidingPosition()
  6919 {
  6920 	if (riding)
  6921 	{
  6922 		position = riding->getRidingPosition();
  6923 		lastPosition = position;
  6924 		rotation.z = riding->getRidingRotation();
  6925 
  6926 		if (riding->getRidingFlip())
  6927 		{
  6928 			if (!isfh())
  6929 				flipHorizontal();
  6930 		}
  6931 		else
  6932 		{
  6933 			if (isfh())
  6934 				flipHorizontal();
  6935 		}
  6936 		//state.wasUnderWater = _isUnderWater;
  6937 	}
  6938 }
  6939 
  6940 void Avatar::adjustHeadRot()
  6941 {
  6942 	if (bone_head)
  6943 	{
  6944 		// 0 to 30 range
  6945 		if (bone_head->rotation.z > 0)
  6946 		{
  6947 			bone_head->internalOffset.x = (bone_head->rotation.z/30.0)*5;
  6948 			//bone_head->internalOffset.y = (bone_head->rotation.z/30.0)*1;
  6949 		}
  6950 		// 0 to -10 range
  6951 		if (bone_head->rotation.z < 0)
  6952 		{
  6953 			bone_head->internalOffset.x = (bone_head->rotation.z/(-10.0))*-4;
  6954 			bone_head->internalOffset.y = (bone_head->rotation.z/(-10.0))*-2;
  6955 		}
  6956 	}
  6957 }
  6958 
  6959 void Avatar::endOfGameState()
  6960 {
  6961 	state.lookAtEntity = 0;
  6962 	setInvincible(true);
  6963 }
  6964 
  6965 bool didRotationFix = true;
  6966 
  6967 void timerEffectStart(Timer *timer, ParticleEffect *effect)
  6968 {
  6969 	if (timer->isActive() && !effect->isRunning())
  6970 	{
  6971 		effect->start();
  6972 	}
  6973 	else if (!timer->isActive() && effect->isRunning())
  6974 	{
  6975 		effect->stop();
  6976 	}
  6977 }
  6978 
  6979 void Avatar::updateFoodParticleEffects()
  6980 {
  6981 	timerEffectStart(&dsq->continuity.speedMultTimer, &speedEmitter);
  6982 	timerEffectStart(&dsq->continuity.defenseMultTimer, &defenseEmitter);
  6983 	timerEffectStart(&dsq->continuity.invincibleTimer, &invincibleEmitter);
  6984 	timerEffectStart(&dsq->continuity.regenTimer, &regenEmitter);
  6985 }
  6986 
  6987 void Avatar::updateLookAt(float dt)
  6988 {
  6989 	//if (dsq->overlay->alpha != 0) return;
  6990 	if (dsq->game->isShuttingDownGameState()) return;
  6991 	if (headTextureTimer > 0)
  6992 	{
  6993 		headTextureTimer -= dt;
  6994 		if (headTextureTimer <= 0)
  6995 		{
  6996 			headTextureTimer = 0;
  6997 			setHeadTexture("");
  6998 		}
  6999 	}
  7000 
  7001 	if (dsq->continuity.form == FORM_FISH)
  7002 	{
  7003 		Bone *b = skeletalSprite.getBoneByIdx(0);
  7004 		if (b)
  7005 			b->setAnimated(Bone::ANIM_ALL);
  7006 		return;
  7007 	}
  7008 
  7009 	const float blinkTime = 5.0;
  7010 	state.blinkTimer += dt;
  7011 	if (state.blinkTimer > blinkTime)
  7012 	{
  7013 		if (lastHeadTexture.empty())
  7014 		{
  7015 			//if (dsq->continuity.form == FORM_NORMAL)
  7016 			setHeadTexture("blink", 0.1);
  7017 			if (chance(50))
  7018 			{
  7019 				state.blinkTimer = blinkTime-0.2;
  7020 			}
  7021 			else
  7022 			{
  7023 				state.blinkTimer = rand()%2;
  7024 			}
  7025 		}
  7026 		else
  7027 		{
  7028 			state.blinkTimer -= dt;
  7029 		}
  7030 	}
  7031 
  7032 	if (bone_head)
  7033 	{
  7034 		const float lookAtTime = 0.8;
  7035 		if (core->mouse.buttons.middle && !state.lockedToWall && isInputEnabled())
  7036 		{
  7037 			didRotationFix = false;
  7038 			bone_head->setAnimated(Bone::ANIM_POS);
  7039 			bone_head->lookAt(dsq->getGameCursorPosition(), lookAtTime, -10, 30, -90);
  7040 			adjustHeadRot();
  7041 		}
  7042 		else
  7043 		{
  7044 			if (state.lookAtEntity && (state.lookAtEntity->isEntityDead() || state.lookAtEntity->isDead() || state.lookAtEntity->isv(EV_LOOKAT,0) || swimming))
  7045 			{
  7046 				state.lookAtEntity = 0;
  7047 			}
  7048 			// find an object of interest
  7049 			if (isv(EV_LOOKAT, 1) && state.lookAtEntity && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->isAnimating() && !state.lockedToWall && !swimming)
  7050 			{
  7051 				didRotationFix = false;
  7052 				bone_head->setAnimated(Bone::ANIM_POS);
  7053 				bone_head->lookAt(state.lookAtEntity->getLookAtPoint(), lookAtTime, -10, 30, -90);
  7054 
  7055 				if (!((state.lookAtEntity->position - position).isLength2DIn(1000)))
  7056 				{
  7057 					state.lookAtEntity = 0;
  7058 				}
  7059 				state.updateLookAtTime += dt;
  7060 				adjustHeadRot();
  7061 			}
  7062 			else
  7063 			{
  7064 				bone_head->setAnimated(Bone::ANIM_ALL);
  7065 
  7066 				if (!didRotationFix && !bone_head->rotationOffset.isInterpolating())
  7067 				{
  7068 					float t = 1;
  7069 					didRotationFix = true;
  7070 					float oldRot = bone_head->rotation.z;
  7071 
  7072 					skeletalSprite.updateBones();
  7073 
  7074 					bone_head->rotationOffset.z = oldRot - bone_head->rotation.z;
  7075 					bone_head->rotationOffset.interpolateTo(Vector(0,0,0), t);
  7076 					bone_head->internalOffset.interpolateTo(Vector(0,0,0), t);
  7077 					/*
  7078 					std::ostringstream os;
  7079 					os << "rotationOffset lerp " << bone_head->rotationOffset.z;
  7080 					debugLog(os.str());
  7081 					*/
  7082 				}
  7083 
  7084 				/*
  7085 				bone_head->rotationOffset = bone_head->rotation.z;
  7086 				bone_head->rotation.z = 0;
  7087 				bone_head->rotationOffset.interpolateTo(0, 0.2);
  7088 				*/
  7089 				//state.updateLookAtTime += dt*10;
  7090 				//state.updateLookAtTime += dt;
  7091 				state.updateLookAtTime += dt*4*2;
  7092 				bone_head->internalOffset.interpolateTo(Vector(0,0), 0.2);
  7093 			}
  7094 
  7095 			if (state.updateLookAtTime > 1.5)
  7096 			{
  7097 				state.lookAtEntity = dsq->game->getNearestEntity(position, 800, this, ET_NOTYPE, DT_NONE, LR_ENTITIES0, LR_ENTITIES2);
  7098 				if (state.lookAtEntity && state.lookAtEntity->isv(EV_LOOKAT, 1))
  7099 				{
  7100 					/*
  7101 					std::ostringstream os;
  7102 					os << "Nearest: " << state.lookAtEntity->name;
  7103 					debugLog(os.str());
  7104 					*/
  7105 
  7106 					state.updateLookAtTime = 0;
  7107 					//if (dsq->continuity.form == FORM_NORMAL)
  7108 					//setHeadTexture("blink", 0.1);
  7109 
  7110 					/*
  7111 					if (state.lookAtEntity->getEntityType() == ET_NEUTRAL)
  7112 					{
  7113 						//if (dsq->continuity.form == FORM_NORMAL)
  7114 						//setHeadTexture("smile", 1);
  7115 					}
  7116 					*/
  7117 
  7118 					if (!state.lookAtEntity->naijaReaction.empty())
  7119 					{
  7120 						setHeadTexture(state.lookAtEntity->naijaReaction, 1.5);
  7121 					}
  7122 				}
  7123 				else
  7124 				{
  7125 					state.lookAtEntity = 0;
  7126 					/*
  7127 					std::ostringstream os;
  7128 					os << state.updateLookAtTime << " : found no entities";
  7129 					debugLog(os.str());
  7130 					*/
  7131 					//state.updateLookAtTime -= 0.3;
  7132 				}
  7133 
  7134 				//skeletalSprite.animate("blink", 2, ANIMLAYER_HEADOVERRIDE);
  7135 			}
  7136 		}
  7137 	}
  7138 }
  7139 
  7140 Vector Avatar::getHeadPosition()
  7141 {
  7142 	if (bone_head)
  7143 		return bone_head->getWorldPosition();
  7144 	return position;
  7145 }
  7146 
  7147 bool lastCursorKeyboard = false;
  7148 
  7149 bool Avatar::isMiniMapCursorOkay()
  7150 {
  7151 //!(dsq->getMouseButtonState(0) || dsq->getMouseButtonState(1))
  7152 	return ((dsq->inputMode != INPUT_MOUSE) ||  (!dsq->game->miniMapRender || !dsq->game->miniMapRender->isCursorIn()));
  7153 }
  7154 
  7155 void Avatar::updateCursorFromKeyboard()
  7156 {
  7157 	/*
  7158 	// why return when singing??
  7159 	//if (isSinging()) return;
  7160 	if (!isInputEnabled()) return;
  7161 
  7162 	Vector diff;
  7163 	int dist = 200;
  7164 	if (isActing(ACTION_SINGLEFT))
  7165 		diff.x = -dist;
  7166 	if (isActing(ACTION_SINGRIGHT))
  7167 		diff.x = dist;
  7168 	if (isActing(ACTION_SINGUP))
  7169 		diff.y = -dist;
  7170 	if (isActing(ACTION_SINGDOWN))
  7171 		diff.y = dist;
  7172 	if (!diff.isZero())
  7173 	{
  7174 		diff.setLength2D(dist);
  7175 		core->mouse.position = Vector(400,300) + diff;
  7176 		lastCursorKeyboard = true;
  7177 	}
  7178 	else if (lastCursorKeyboard)
  7179 	{
  7180 		debugLog("HEY!: lastCursorKeyboard mouse position reset");
  7181 		core->mouse.position = Vector(400,300);
  7182 		lastCursorKeyboard = false;
  7183 		dsq->toggleCursor(false, 0.2);
  7184 	}
  7185 
  7186 	//!diff.isZero()  || 
  7187 	if (isInputEnabled() && (!core->mouse.change.isZero()))
  7188 	{
  7189 		dsq->toggleCursor(true, 0.2);
  7190 	}
  7191 	*/
  7192 }
  7193 
  7194 void Avatar::onUpdate(float dt)
  7195 {
  7196 	BBGE_PROF(Avatar_onUpdate);
  7197 
  7198 	// animation debug code
  7199 	/*
  7200 	for (int i = 0; i < 8; i++)
  7201 	{
  7202 		if (skeletalSprite.getAnimationLayer(i))
  7203 		{
  7204 			if (skeletalSprite.getAnimationLayer(i)->isAnimating())
  7205 			{
  7206 				std::ostringstream os;
  7207 				os << "anim layer: " << i << " - " << skeletalSprite.getAnimationLayer(i)->getCurrentAnimation()->name;
  7208 				debugLog(os.str());
  7209 				//debugLog("anim layer 0: " + skeletalSprite.getAnimationLayer(0)->getCurrentAnimation()->name);
  7210 			}
  7211 		}
  7212 	}
  7213 	*/
  7214 
  7215 	looking = 0;
  7216 
  7217 #ifdef AQ_TEST_QUADTRAIL
  7218 	quadTrail->addPoint(position);
  7219 #endif
  7220 
  7221 	if (lightFormGlow)
  7222 	{
  7223 		if (dsq->continuity.light)
  7224 		{
  7225 			lightFormGlow->scale = Vector(6,6) + Vector(4,4)*dsq->continuity.light;
  7226 		}
  7227 		else
  7228 		{
  7229 			lightFormGlow->scale = Vector(6,6);
  7230 		}
  7231 	}
  7232 
  7233 	applyRidingPosition();
  7234 	if (activateEntity)
  7235 	{
  7236 		activateEntity->activate();
  7237 		activateEntity = 0;
  7238 	}
  7239 	if (bone_head)
  7240 		headPosition = bone_head->getWorldPosition();
  7241 
  7242 	//vel /= 0;
  7243 	if (vel.isNan())
  7244 	{
  7245 		debugLog("detected velocity NaN");
  7246 		vel = Vector(0,0);
  7247 	}
  7248 
  7249 	if (canWarpDelay > 0)
  7250 	{
  7251 		canWarpDelay = canWarpDelay - dt;
  7252 		if (canWarpDelay < 0)
  7253 		{
  7254 			canWarp = true;
  7255 			canWarpDelay = 0;
  7256 		}
  7257 	}
  7258 
  7259 	if (fireDelay > 0)
  7260 	{
  7261 		fireDelay -= dt;
  7262 		if (fireDelay < 0)
  7263 		{
  7264 			fireDelay = 0;
  7265 		}
  7266 	}
  7267 
  7268 	if (doubleClickDelay > 0)
  7269 	{
  7270 		doubleClickDelay = doubleClickDelay - dt;
  7271 		if (doubleClickDelay < 0) doubleClickDelay = 0;
  7272 	}
  7273 
  7274 
  7275 	if (isInputEnabled())
  7276 	{
  7277 		if (web)
  7278 		{
  7279 			if (!webBitTimer.isActive())
  7280 			{
  7281 				webBitTimer.start(0.5);
  7282 			}
  7283 			web->setPoint(curWebPoint, position);
  7284 
  7285 			if (webBitTimer.updateCheck(dt))
  7286 			{
  7287 				webBitTimer.start(0.5);
  7288 
  7289 				curWebPoint = web->addPoint(position);
  7290 			}
  7291 		}
  7292 		
  7293 		if (!dsq->game->isPaused() && isActing(ACTION_LOOK) && !dsq->game->avatar->isSinging() && dsq->game->avatar->isInputEnabled() && !dsq->game->isInGameMenu())
  7294 		{
  7295 			looking = 1;
  7296 		}
  7297 		else
  7298 		{
  7299 			looking = 0;
  7300 		}
  7301 	}
  7302 	else
  7303 	{
  7304 		looking = 0;
  7305 	}
  7306 
  7307 
  7308 
  7309 	// setup shader
  7310 	if (core->afterEffectManager)
  7311 	{
  7312 
  7313 		/*
  7314 		if (!_isUnderWater)
  7315 		{
  7316 			core->afterEffectManager->setActiveShader(AS_WASHOUT);
  7317 			//core->afterEffectManager->setActiveShader(AS_NONE);
  7318 		}
  7319 		else
  7320 		*/
  7321 		if (dsq->user.video.shader != AS_NONE)
  7322 		{
  7323 			core->afterEffectManager->setActiveShader((ActiveShader)dsq->user.video.shader);
  7324 		}
  7325 		else
  7326 		{
  7327 			if (damageTimer.isActive() && dsq->isShakingCamera())
  7328 			{
  7329 				if (dsq->user.video.blur)
  7330 					core->afterEffectManager->setActiveShader(AS_BLUR);
  7331 			}
  7332 			else
  7333 			{
  7334 				core->afterEffectManager->setActiveShader(AS_NONE);
  7335 			}
  7336 
  7337 		}
  7338 	}
  7339 
  7340 	/*
  7341 	if (!targets.empty())
  7342 	{
  7343 		if (targets[0] && (targets[0]->position - this->position).getSquaredLength2D() > sqr(TARGET_RANGE))
  7344 		{
  7345 			clearTargets();
  7346 		}
  7347 	}
  7348 	*/
  7349 	//spawnChildClone(4);
  7350 	if (!core->cameraRot.isInterpolating())
  7351 		// 10
  7352 		core->cameraRot.interpolateTo(Vector(0,0,360), 30, -1);
  7353 	/*
  7354 	for (int i = 0; i < targets.size(); i++)
  7355 	{
  7356 		if (targets[i] && !this->isEntityDead())
  7357 		{
  7358 			targetQuads[i]->alpha.interpolateTo(1,0.1);
  7359 			targetQuads[i]->position = targets[i]->position;
  7360 		}
  7361 		else
  7362 		{
  7363 			if (targetQuads[i]->alpha.getValue()>0)
  7364 				targetQuads[i]->alpha.interpolateTo(0,0.1);
  7365 		}
  7366 	}
  7367 	*/
  7368 
  7369 	Entity::onUpdate(dt);
  7370 
  7371 	if (isEntityDead() && skeletalSprite.getCurrentAnimation()->name != "dead")
  7372 	{
  7373 		fallOffWall();
  7374 		biteLeftEmitter.stop();
  7375 		biteRightEmitter.stop();
  7376 		wakeEmitter.stop();
  7377 		rollLeftEmitter.stop();
  7378 		rollRightEmitter.stop();
  7379 		dsq->game->toggleOverrideZoom(false);
  7380 		if (dsq->continuity.form != FORM_NORMAL)
  7381 			changeForm(FORM_NORMAL);
  7382 		setHeadTexture("Pain");
  7383 		core->globalScale.interpolateTo(Vector(5,5),3);
  7384 		rotation.interpolateTo(Vector(0,0,0), 0.1);
  7385 		skeletalSprite.animate("dead");
  7386 	}
  7387 	if (isEntityDead())
  7388 	{
  7389 		dsq->game->toggleOverrideZoom(false);
  7390 	}
  7391 
  7392 	if (dsq->user.control.targeting)
  7393 		updateTargets(dt, false);
  7394 	else
  7395 		targets.clear();
  7396 
  7397 	updateTargetQuads(dt);
  7398 
  7399 	updateDualFormGlow(dt);
  7400 	updateLookAt(dt);
  7401 
  7402 	updateFoodParticleEffects();
  7403 
  7404 	if (!dsq->game->isPaused())
  7405 		myZoom.update(dt);
  7406 
  7407 	_isUnderWater = isUnderWater();
  7408 
  7409 	// JUMPING OUT
  7410 	if (!_isUnderWater && state.wasUnderWater)
  7411 	{
  7412 
  7413 		// "falling" out, not bursting out
  7414 		int fallOutSpeed = 200;
  7415 		/*
  7416 		if (waterBubble)
  7417 			fallOutSpeed = 400;
  7418 		*/
  7419 		bool waterBubbleRect = (waterBubble && waterBubble->pathShape == PATHSHAPE_RECT);
  7420 
  7421 		//&& !waterBubbleRect
  7422 		if (!riding && (!bursting && vel.isLength2DIn(fallOutSpeed)))
  7423 		{
  7424 
  7425 			if (waterBubble)
  7426 			{
  7427 				// prevent from falling out
  7428 				// if circle, clamp
  7429 				waterBubble->clampPosition(&position);
  7430 				vel *= 0.5;
  7431 				startBurstCommon();
  7432 			}
  7433 			else
  7434 			{
  7435 				if (!dsq->game->waterLevel.isInterpolating())
  7436 				{
  7437 					if (vel.y < 0)
  7438 						vel.y = -vel.y*0.5;
  7439 					position.y = dsq->game->waterLevel.x + collideRadius;
  7440 				}
  7441 			}
  7442 		}
  7443 		else
  7444 		{
  7445 			if (waterBubble)
  7446 				lastJumpOutFromWaterBubble = true;
  7447 			else
  7448 				lastJumpOutFromWaterBubble = false;
  7449 
  7450 			lastWaterBubble = waterBubble;
  7451 			waterBubble = 0;
  7452 			BBGE_PROF(Avatar_splashOut);
  7453 			splash(false);
  7454 
  7455 			if (dsq->continuity.form != FORM_FISH)
  7456 			{
  7457 				vel *= vars->jumpVelocityMod; // 1.25;
  7458 				vel.capLength2D(2000);
  7459 				currentMaxSpeed *= 2.0;
  7460 			}
  7461 			else
  7462 			{
  7463 				vel *= 1.5;
  7464 				vel.capLength2D(1500);
  7465 				currentMaxSpeed *= 1.5;
  7466 			}
  7467 
  7468 			// total max speed
  7469 			fallGravityTimer = 0.0;
  7470 
  7471 			wakeEmitter.stop();
  7472 			biteTimer = 0;
  7473 			//stopBurst();
  7474 
  7475 			// if first time
  7476 			if (!dsq->mod.isActive() && dsq->continuity.getFlag("leftWater")==0 && dsq->game->sceneName.find("veil")!=std::string::npos)
  7477 			{
  7478 				setInvincible(true);
  7479 				setv(EV_NOINPUTNOVEL, 0);
  7480 
  7481 				setWasUnderWater();
  7482 
  7483 				if (vel.y > -500)
  7484 					vel.y = -500;
  7485 				dsq->continuity.setFlag("leftWater", 1);
  7486 
  7487 				core->sound->fadeMusic(SFT_OUT, 2);
  7488 				//("Veil");
  7489 				dsq->game->avatar->disableInput();
  7490 				dsq->gameSpeed.interpolateTo(0.1, 0.5);
  7491 
  7492 				//dsq->sound->setMusicFader(0.5, 0.5);
  7493 				core->sound->playSfx("NaijaGasp");
  7494 				core->main(0.75);
  7495 
  7496 				
  7497 
  7498 				dsq->voiceOnce("Naija_VeilCrossing");
  7499 				core->main(10*0.1);
  7500 
  7501 				dsq->gameSpeed.interpolateTo(1, 0.2);
  7502 
  7503 				dsq->sound->playMusic("Veil", SLT_LOOP, SFT_CROSS, 20);
  7504 
  7505 				//dsq->sound->setMusicFader(1.0, 1);
  7506 
  7507 				dsq->game->avatar->enableInput();
  7508 
  7509 				setv(EV_NOINPUTNOVEL, 1);
  7510 
  7511 				setInvincible(false);
  7512 
  7513 				//dsq->continuity.setFlag("leftWater", 0);
  7514 
  7515 			}
  7516 
  7517 
  7518 			state.outOfWaterTimer = 0;
  7519 			state.outOfWaterVel = vel;
  7520 			//startBackFlip();
  7521 		}
  7522 
  7523 		if (currentMaxSpeed > dsq->v.maxOutOfWaterSpeed)
  7524 		{
  7525 			currentMaxSpeed = dsq->v.maxOutOfWaterSpeed;
  7526 		}
  7527 		if (currentMaxSpeed < 1200)
  7528 		{
  7529 			currentMaxSpeed = 1200;
  7530 		}
  7531 	}
  7532 	// JUMPING IN
  7533 	else if (_isUnderWater && !state.wasUnderWater)
  7534 	{
  7535 		// falling in
  7536 		splash(true);
  7537 
  7538 		lastOutOfWaterMaxSpeed = getMaxSpeed();
  7539 		lastOutOfWaterMaxSpeed *= 0.75;
  7540 
  7541 		if (lastOutOfWaterMaxSpeed > 1000)
  7542 			lastOutOfWaterMaxSpeed = 1000;
  7543 
  7544 		fallGravityTimer = 0.5;
  7545 		if (rolling)
  7546 			fallGravityTimer *= 1.5;
  7547 		stopBurst();
  7548 
  7549 		if (state.backFlip)
  7550 		{
  7551 			stopBackFlip();
  7552 		}
  7553 
  7554 	}
  7555 
  7556 	//skeletalSprite.getBoneByIdx(3)->getWorldPosition();
  7557 
  7558 	state.wasUnderWater = _isUnderWater;
  7559 
  7560 	if (!_isUnderWater)
  7561 	{
  7562 		state.outOfWaterTimer += dt;
  7563 		if (state.outOfWaterTimer > 100)
  7564 			state.outOfWaterTimer = 100;
  7565 	}
  7566 
  7567 
  7568 	if (!state.backFlip && !_isUnderWater && state.outOfWaterTimer < 0.1 && !riding && !boneLock.on)
  7569 	{
  7570 		const int check = 64;
  7571 		Vector m = getVectorToCursor();
  7572 		if (state.outOfWaterVel.x < 0 && m.x > check)
  7573 		{
  7574 			startBackFlip();
  7575 		}
  7576 		if (state.outOfWaterVel.x > 0 && m.x < -check)
  7577 		{
  7578 			startBackFlip();
  7579 		}
  7580 	}
  7581 
  7582 	/*
  7583 	if (core->afterEffectManager && _isUnderWater)
  7584 	{
  7585 		if (swimming && vel.getSquaredLength2D() > sqr(200))
  7586 		{
  7587 			rippleTimer += dt;
  7588 			while (rippleTimer > RIPPLE_INTERVAL)
  7589 			{
  7590 				// 0.01   20
  7591 				core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.05,0.08,15,0.2f, 1.2));
  7592 
  7593 				//core->afterEffectManager->addEffect(new ShockEffect(Vector(400,300),0.01,0.002f,15,0.1f));
  7594 				rippleTimer = 0;
  7595 			}
  7596 		}
  7597 	}
  7598 	*/
  7599 
  7600 
  7601 	/*
  7602 	bobTimer += dt*2;
  7603 	offset.y = sin(bobTimer)*5 - 2.5f;
  7604 	*/
  7605 
  7606 	if (isEntityDead())
  7607 	{
  7608 		updateHair(dt);
  7609 	}
  7610 
  7611 	if (isEntityDead()) return;
  7612 
  7613 	if (flourishTimer.updateCheck(dt))
  7614 	{
  7615 		flourish = 0;
  7616 		rotationOffset.z = 0;
  7617 	}
  7618 
  7619 	if (isInputEnabled())
  7620 		stillTimer.update(dt);
  7621 
  7622 	if (vel.isZero()) //&& !isSinging())
  7623 	{
  7624 		if (!stillTimer.isActive())
  7625 		{
  7626 			stillTimer.startStopWatch();
  7627 			//debugLog("start stillTimer");
  7628 		}
  7629 	}
  7630 	else
  7631 	{
  7632 		stillTimer.stop();
  7633 	}
  7634 
  7635 	flourishPowerTimer.updateCheck(dt);
  7636 
  7637 	if (isSinging())
  7638 	{
  7639 		if (songInterfaceTimer < 1)
  7640 			songInterfaceTimer += dt;
  7641 	}
  7642 	updateJoystick(dt);
  7643 
  7644 	if (quickSongCastDelay>0)
  7645 	{
  7646 		quickSongCastDelay -= dt;
  7647 		if (quickSongCastDelay < 0)
  7648 			quickSongCastDelay = 0;
  7649 	}
  7650 	if (ripples && _isUnderWater)
  7651 	{
  7652 		rippleDelay -= dt;
  7653 		if (rippleDelay < 0)
  7654 		{
  7655 			if (core->afterEffectManager)
  7656 				core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.04,0.06,15,0.2f));
  7657 			rippleDelay = 0.15;
  7658 		}
  7659 	}
  7660 
  7661 	if (dsq->continuity.tripTimer.isActive())
  7662 	{
  7663 		static int tripCount = 0;
  7664 		tripDelay -= dt;
  7665 		if (tripDelay < 0)
  7666 		{
  7667 ;
  7668 			tripDelay = 0.15;
  7669 			tripCount ++;
  7670 			if (tripCount > 10)
  7671 			{
  7672 				/*
  7673 				// hacktastic
  7674 				EMOTE_NAIJAEVILLAUGH	= 0
  7675 				EMOTE_NAIJAGIGGLE		= 1
  7676 				EMOTE_NAIJALAUGH		= 2
  7677 				EMOTE_NAIJASADSIGH		= 3
  7678 				EMOTE_NAIJASIGH			= 4
  7679 				EMOTE_NAIJAWOW			= 5
  7680 				EMOTE_NAIJAUGH			= 6
  7681 				*/
  7682 				float p = dsq->continuity.tripTimer.getPerc();
  7683 				if (p > 0.6)
  7684 				{
  7685 					if (core->afterEffectManager)
  7686 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.04,0.06,15,0.2f));
  7687 				}
  7688 				else
  7689 				{
  7690 					if (core->afterEffectManager)
  7691 						core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position+offset,0.4,0.6,15,0.2f));
  7692 				}
  7693 				if (p > 0.75){}
  7694 				else if (p > 0.5)
  7695 				{
  7696 					dsq->shakeCamera(2, 4);
  7697 					if (chance(80))
  7698 					{
  7699 						if (chance(60))
  7700 							dsq->emote.playSfx(2);
  7701 						else
  7702 							dsq->emote.playSfx(0);
  7703 					}
  7704 				}
  7705 				else
  7706 				{
  7707 					if (p < 0.2)
  7708 						dsq->shakeCamera(10, 4);
  7709 					else
  7710 						dsq->shakeCamera(5, 4);
  7711 					tripper->color.interpolateTo(Vector(1, 0.2, 0.2), 3);
  7712 					if (chance(75))
  7713 						dsq->emote.playSfx(6);
  7714 				}
  7715 
  7716 				tripCount = 0;
  7717 			}
  7718 		}
  7719 	}
  7720 
  7721 	if (position.isInterpolating())
  7722 	{
  7723 		lastPosition = position;
  7724 	}
  7725 
  7726 
  7727 	updateCursorFromKeyboard();
  7728 	updateFormVisualEffects(dt);
  7729 	updateShock(dt);
  7730 	updateRoll(dt);
  7731 	updateTummy(dt);
  7732 	updateWallJump(dt);
  7733 
  7734 	if (dsq->autoSingMenuOpen)
  7735 	{
  7736 		if (micNote != -1 && !this->isSinging())
  7737 		{
  7738 			openedFromMicInput = true;
  7739 			openSingingInterface();
  7740 		}
  7741 		if (micNote == -1 && isSinging() && openedFromMicInput)
  7742 		{
  7743 			openedFromMicInput = false;
  7744 			closeSingingInterface();
  7745 		}
  7746 	}
  7747 
  7748 	if (formAbilityDelay > 0)
  7749 	{
  7750 		formAbilityDelay -= dt;
  7751 		if (formAbilityDelay < 0)
  7752 			formAbilityDelay = 0;
  7753 	}
  7754 	//updateCursor(dt);
  7755 
  7756 	if (getState() == STATE_PUSH)
  7757 	{
  7758 		/*
  7759 		if (rotation.z < 0)
  7760 			rotation.z += 360;
  7761 		if (rotation.z > 360)
  7762 			rotation.z -= 360;
  7763 		*/
  7764 		rotateToVec(vel, 0, -90);
  7765 		if (vel.x < 0&& !isfh())
  7766 			flipHorizontal();
  7767 		else if (vel.x > 0 && isfh())
  7768 			flipHorizontal();
  7769 	}
  7770 
  7771 	updateAura(dt);
  7772 	switch(ropeState)
  7773 	{
  7774 	case 1:
  7775 	{
  7776 		if (ropeTimer > 0)
  7777 		{
  7778 			ropeTimer -= dt;
  7779 			if (ropeTimer < 0)
  7780 			{
  7781 				ropeState = 0;
  7782 				ropeTimer = 0;
  7783 			}
  7784 		}
  7785 		ropePos += ropeVel*dt;
  7786 		if (dsq->game->isObstructed(TileVector(ropePos)))
  7787 		{
  7788 			ropeState = 2;
  7789 			ropeTimer = 2;
  7790 		}
  7791 		std::ostringstream os;
  7792 		os << "ropePos (" << ropePos.x << ", " << ropePos.y << ")";
  7793 		debugLog(os.str());
  7794 	}
  7795 	break;
  7796 	case 2:
  7797 		if (ropeTimer > 0)
  7798 		{
  7799 			ropeTimer -= dt;
  7800 			if (ropeTimer < 0)
  7801 			{
  7802 				ropeState = 0;
  7803 				ropeTimer = 0;
  7804 			}
  7805 		}
  7806 		Vector add = (ropePos - position);
  7807 		if (add.getSquaredLength2D() > sqr(200))
  7808 		{
  7809 			add.setLength2D(4000);
  7810 			vel += add*dt;
  7811 		}
  7812 	break;
  7813 	}
  7814 
  7815 
  7816 	updateSingingInterface(dt);
  7817 
  7818 	if (pullTarget)
  7819 	{
  7820 		if (pullTarget->life < 1 || !pullTarget->isPullable())
  7821 		{
  7822 			pullTarget->stopPull();
  7823 			pullTarget = 0;
  7824 		}
  7825 		else
  7826 		{
  7827 			/*
  7828 			static float c = 0;
  7829 			c += dt;
  7830 			if (c > 0.2)
  7831 			{
  7832 				EnergyTendril *t = new EnergyTendril(avatar->position, pullTarget->position);
  7833 				core->addRenderObject(t, LR_PARTICLES);
  7834 				c -= dt;
  7835 			}
  7836 			*/
  7837 		}
  7838 	}
  7839 
  7840 	formTimer += dt;
  7841 
  7842 	/*
  7843 	if (formTimer > 2.0 && dsq->continuity.form == FORM_SPIRIT)
  7844 	{
  7845 		changeForm(FORM_NORMAL, true);
  7846 	}
  7847 	*/
  7848 	if (pickingPullTarget)
  7849 	{
  7850 		//debugLog("picking pull target");
  7851 		Entity *closest = 0;
  7852 		int smallestDist = -1;
  7853 		FOR_ENTITIES(i)
  7854 		{
  7855 			Entity *e = *i;
  7856 
  7857 			if (e->isPullable() && e->life == 1)
  7858 			{
  7859 				if (e->isCoordinateInside(dsq->getGameCursorPosition()))
  7860 				{
  7861 					int dist = (e->position - dsq->getGameCursorPosition()).getSquaredLength2D();
  7862 					if (dist < smallestDist || smallestDist == -1)
  7863 					{
  7864 						smallestDist = dist;
  7865 						closest = e;
  7866 					}
  7867 				}
  7868 			}
  7869 		}
  7870 		potentialPullTarget = closest;
  7871 	}
  7872 
  7873 	if (dsq->continuity.form == FORM_SPIRIT)
  7874 	{
  7875 		if (useSpiritDistance)
  7876 		{
  7877 			if (formTimer > 1)
  7878 			{
  7879 				if (!(bodyPosition - position).isLength2DIn(SPIRIT_RANGE))
  7880 				{
  7881 					changeForm(FORM_NORMAL);
  7882 				}
  7883 			}
  7884 		}
  7885 		// here
  7886 		if (!_isUnderWater)
  7887 		{
  7888 			changeForm(FORM_NORMAL);
  7889 		}
  7890 	}
  7891 
  7892 	// revert stuff
  7893 	float revertGrace = 0.4;
  7894 	static bool revertButtonsAreDown = false;
  7895 	if (inputEnabled && (dsq->inputMode == INPUT_KEYBOARD || dsq->inputMode == INPUT_MOUSE) && (!pathToActivate && !entityToActivate))
  7896 	{
  7897 		//debugLog("update stuff");
  7898 		///*&& dsq->continuity.form != FORM_SPIRIT*/
  7899 		//|| isActing(ACTION_PRIMARY)
  7900 		//|| isActing(ACTION_SECONDARY)
  7901 		if (dsq->continuity.form != FORM_NORMAL && (core->mouse.pure_buttons.left && core->mouse.pure_buttons.right) && getVectorToCursor(true).isLength2DIn(minMouse))
  7902 		{
  7903 			if (!revertButtonsAreDown)
  7904 			{
  7905 				revertTimer = revertGrace;
  7906 				revertButtonsAreDown = true;
  7907 			}
  7908 			else if (revertButtonsAreDown)
  7909 			{
  7910 				if (revertTimer > 0)
  7911 				{
  7912 					revertTimer -= dt;
  7913 					if (revertTimer < 0)
  7914 					{
  7915 						revertTimer = 0;
  7916 					}
  7917 				}
  7918 			}
  7919 		}
  7920 		//&& !isActing(ACTION_PRIMARY) && !isActing(ACTION_SECONDARY)
  7921 		else if ((!core->mouse.pure_buttons.left && !core->mouse.pure_buttons.right))
  7922 		{
  7923 			if (revertTimer > 0 && getVectorToCursor(true).isLength2DIn(minMouse) && state.spellCharge < revertGrace+0.1)
  7924 			{
  7925 				revert();
  7926 				//changeForm(FORM_NORMAL);
  7927 			}
  7928 			revertButtonsAreDown = false;
  7929 			revertTimer = 0;
  7930 		}
  7931 	}
  7932 	else
  7933 	{
  7934 		revertButtonsAreDown = false;
  7935 	}
  7936 
  7937 	/*
  7938 	if (this->state.crawlingOnWall)
  7939 	{
  7940 		if (isActing("a1"))
  7941 		{
  7942 			wallNormal = dsq->game->getWallNormal(position);
  7943 			if (wallNormal.dot2D(lastWallNormal)<=0.3)
  7944 			{
  7945 				stopWallCrawl();
  7946 			}
  7947 			else
  7948 			{
  7949 				Vector left = wallNormal.getPerpendicularLeft();
  7950 				Vector right = wallNormal.getPerpendicularRight();
  7951 
  7952 
  7953 				Vector test = getVectorToCursor();
  7954 				if (!test.isLength2DIn(64))
  7955 				{
  7956 					test.normalize2D();
  7957 
  7958 					Vector move;
  7959 					if (test.dot2D(left)>0)
  7960 						move = left;
  7961 					else
  7962 						move = right;
  7963 
  7964 					move.setLength2D(800);
  7965 
  7966 					if (move.x > 0 && !isfh())
  7967 						flipHorizontal();
  7968 					if (move.x < 0 && isfh())
  7969 						flipHorizontal();
  7970 
  7971 					position += move*dt;
  7972 					rotateToVec(wallNormal, 0.1);
  7973 				}
  7974 				else
  7975 				{
  7976 					stopWallCrawl();
  7977 				}
  7978 			}
  7979 		}
  7980 		else
  7981 		{
  7982 			stopWallCrawl();
  7983 		}
  7984 	}
  7985 	*/
  7986 
  7987 	//if (core->getNestedMains() == 1)
  7988 	{
  7989 		if (leaches > 3)
  7990 		{
  7991 			/*
  7992 			const float leachHurtInterval = 3;
  7993 			state.leachTimer += dt;
  7994 			if (state.leachTimer > leachHurtInterval)
  7995 			{
  7996 				state.leachTimer -= leachHurtInterval;
  7997 				DamageData d;
  7998 				d.damage = int(leaches/3);
  7999 				damage(d);
  8000 				//hit(0, 0, SPELL_NONE, int(leaches/3));
  8001 			}
  8002 			*/
  8003 		}
  8004 
  8005 
  8006 		if (getState() != STATE_TRANSFORM && dsq->continuity.getWorldType() == WT_NORMAL)
  8007 		//if (dsq->continuity.form == FORM_ENERGY)
  8008 		{
  8009 			//if (dsq->continuity.selectedSpell == SPELL_SHOCK)
  8010 
  8011 			formAbilityUpdate(dt);
  8012 
  8013 			// is this really necessary??
  8014 			// YES!
  8015 			// this allows the player to start charging quickly after firing
  8016 			/*
  8017 			if (isActing("charge") && !charging && spellCastDelay == 0 && inputEnabled)
  8018 			{
  8019 				this->rmbd();
  8020 			}
  8021 			*/
  8022 			// maybe not useful anymore
  8023 			/*
  8024 			if (!isActing("charge") && charging && spellChargeDelay == 0 && inputEnabled)
  8025 			{
  8026 			}
  8027 			*/
  8028 		}
  8029 
  8030 		if (state.useItemDelay.updateCheck(dt))
  8031 		{
  8032 		}
  8033 
  8034 		ActionMapper::onUpdate(dt);
  8035 
  8036 		if (inputEnabled)
  8037 		{
  8038 			if (state.blind)
  8039 			{
  8040 				if (state.blindTimer.updateCheck(dt))
  8041 				{
  8042 					state.blind = false;
  8043 					removeBlindEffects();
  8044 				}
  8045 			}
  8046 
  8047 			 /*&& this->getSelectedSpell() == SPELL_ENERGYBLAST*/
  8048 			/*
  8049 			// HACK: hacked out for now
  8050 			// FINDTARGET
  8051 
  8052 			if (charging && !targets.empty() && targets[0] == 0 && state.spellCharge > 0.2
  8053 				&& dsq->continuity.form == FORM_ENERGY)
  8054 			{
  8055 				for (int i = 0; i < dsq->entities.size(); i++)
  8056 				{
  8057 					Entity *e = dsq->entities[i];
  8058 					if (e && e != this && e->isAvatarAttackTarget() && !e->isEntityDead() && e->isAffectedBySpell(SPELL_ENERGYBLAST))
  8059 					{
  8060 						ScriptedEntity *se = (ScriptedEntity*)e;
  8061 						if ((e->position - dsq->getGameCursorPosition()).getSquaredLength2D() < sqr(64))
  8062 						{
  8063 							targets[0] = e;
  8064 						}
  8065 					}
  8066 				}
  8067 			}
  8068 			*/
  8069 		}
  8070 
  8071 
  8072 		if (boneLock.entity != 0)
  8073 		{
  8074 			/*
  8075 			std::ostringstream os;
  8076 			os << "boneLock.wallNormal(" << boneLock.wallNormal.x << ", " << boneLock.wallNormal.y << ")";
  8077 			debugLog(os.str());
  8078 			*/
  8079 			if (!_isUnderWater && !(boneLock.wallNormal.y < -0.03))
  8080 			{
  8081 				if (lockToWallFallTimer == -1)
  8082 					lockToWallFallTimer = 0.4;
  8083 			}
  8084 			else
  8085 				lockToWallFallTimer = -1;
  8086 		}
  8087 
  8088 		if (lockToWallFallTimer > 0)
  8089 		{
  8090 			lockToWallFallTimer -= dt;
  8091 			if (lockToWallFallTimer <= 0)
  8092 			{
  8093 				fallOffWall();
  8094 			}
  8095 		}
  8096 
  8097 		if (spellCastDelay > 0)
  8098 		{
  8099 			spellCastDelay -= dt;
  8100 			if (spellCastDelay <= 0)
  8101 			{
  8102 				spellCastDelay = 0;
  8103 			}
  8104 		}
  8105 
  8106 		if (state.lockToWallDelay.updateCheck(dt))
  8107 		{
  8108 		}
  8109 
  8110 		if (state.tapTimer.updateCheck(dt))
  8111 		{
  8112 			tapped = "";
  8113 		}
  8114 		if (pushingOffWallEffect > 0)
  8115 		{
  8116 			pushingOffWallEffect -= dt;
  8117 			if (pushingOffWallEffect <= 0)
  8118 			{
  8119 				lastLockToWallPos = Vector(0,0);
  8120 				pushingOffWallEffect = 0;
  8121 				if (vel.getSquaredLength2D() > sqr(1200))
  8122 				{
  8123 					vel.setLength2D(1200);
  8124 				}
  8125 			}
  8126 		}
  8127 		/*
  8128 		if (beamFiring)
  8129 		{
  8130 			// collides enemies with beam as well
  8131 			beam->width = getBeamWidth();
  8132 			Vector diff = dsq->getGameCursorPosition() - this->position;
  8133 			diff |= beam->width.getValue()/2.0f;
  8134 			beam->position = this->position + diff;
  8135 			float angle=0;
  8136 			MathFunctions::calculateAngleBetweenVectorsInDegrees(this->position, dsq->getGameCursorPosition(), angle);
  8137 			beam->rotation.z = angle+90;
  8138 			beam->position.z = 3;
  8139 			//collideBeamWithEntities();
  8140 		}
  8141 		*/
  8142 		if (state.dodgeEffectTimer.updateCheck(dt))
  8143 		{
  8144 			vel.capLength2D(vars->maxSwimSpeed);
  8145 			/*
  8146 			if (vel.getSquaredLength2D() > sqr(vars->maxSwimSpeed))
  8147 				vel.setLength2D(vars->maxSwimSpeed);
  8148 			*/
  8149 		}
  8150 		/*
  8151 		if (dodgeEffectTimer > 0)
  8152 		{
  8153 			dodgeEffectTimer -= dt;
  8154 			if (dodgeEffectTimer <= 0)
  8155 			{
  8156 				dodgeEffectTimer = 0;
  8157 				if (vel.getSquaredLength2D() > sqr(vars->maxSwimSpeed))
  8158 					vel |= vars->maxSwimSpeed;
  8159 			}
  8160 		}
  8161 		*/
  8162 		if (dodgeDelay > 0)
  8163 		{
  8164 			dodgeDelay -= dt;
  8165 			if (dodgeDelay <= 0)
  8166 			{
  8167 				dodgeDelay = 0;
  8168 			}
  8169 		}
  8170 
  8171 		if (text)
  8172 		{
  8173 			text->position = position + Vector(100);
  8174 		}
  8175 
  8176 		if (charging)
  8177 		{
  8178 			/*
  8179 			chargeGraphic->position = this->position;
  8180 			chargeGraphic->position.z = position.z + 0.05;
  8181 			*/
  8182 			state.spellCharge += dt;
  8183 			switch (dsq->continuity.form)
  8184 			{
  8185 			case FORM_SUN:
  8186 			{
  8187 				if (state.spellCharge > 1.5 && chargeLevelAttained <1)
  8188 				{
  8189 					chargeLevelAttained = 1.5;
  8190 					core->sound->playSfx("PowerUp");
  8191 					chargingEmitter->load("ChargingEnergy2");
  8192 				}
  8193 			}
  8194 			break;
  8195 			case FORM_DUAL:
  8196 			{
  8197 				if (state.spellCharge >= 1.4 && chargeLevelAttained<1)
  8198 				{
  8199 					chargeLevelAttained = 1;
  8200 
  8201 					core->sound->playSfx("PowerUp");
  8202 					//debugLog("charge visual effect 2");
  8203 					chargeEmitter->load("ChargeDualForm");
  8204 					chargeEmitter->start();
  8205 
  8206 					chargingEmitter->load("ChargedDualForm");
  8207 					chargingEmitter->start();
  8208 
  8209 
  8210 
  8211 					//chargeVisualEffect("particles/energy-charge-2");
  8212 				}
  8213 				/*
  8214 				if (state.spellCharge >= 1.5 && chargeLevelAttained<2)
  8215 				{
  8216 					chargeLevelAttained = 2;
  8217 
  8218 					core->sound->playSfx("PowerUp");
  8219 					//debugLog("charge visual effect 2");
  8220 					chargeEmitter->load("EnergyCharge");
  8221 					chargeEmitter->start();
  8222 
  8223 					//chargeVisualEffect("particles/energy-charge-2");
  8224 				}
  8225 				*/
  8226 			}
  8227 			break;
  8228 			case FORM_ENERGY:
  8229 			{
  8230 				/*
  8231 				if (state.spellCharge >= 0.99 && chargeLevelAttained<1)
  8232 				{
  8233 					chargeLevelAttained = 1;
  8234 					debugLog("charge visual effect 1");
  8235 					chargeVisualEffect("energy-charge-1");
  8236 
  8237 				}
  8238 				*/
  8239 				if (state.spellCharge >= 1.5 && chargeLevelAttained<2)
  8240 				{
  8241 					chargeLevelAttained = 2;
  8242 					core->sound->playSfx("PowerUp");
  8243 					//debugLog("charge visual effect 2");
  8244 					chargeEmitter->load("ChargeEnergy");
  8245 					chargeEmitter->start();
  8246 
  8247 
  8248 					chargingEmitter->load("ChargingEnergy2");
  8249 					//chargeVisualEffect("particles/energy-charge-2");
  8250 				}
  8251 			}
  8252 			break;
  8253 			case FORM_NATURE:
  8254 			{
  8255 				if (state.spellCharge >= 0.9 && chargeLevelAttained<2)
  8256 				{
  8257 					chargeLevelAttained = 2;
  8258 					core->sound->playSfx("PowerUp");
  8259 					chargeEmitter->load("ChargeNature2");
  8260 					chargeEmitter->start();
  8261 
  8262 					chargingEmitter->load("ChargingNature2");
  8263 					chargingEmitter->start();
  8264 				}
  8265 				/*
  8266 				if (state.spellCharge >= 0.5 && chargeLevelAttained<1)
  8267 				{
  8268 					chargeLevelAttained = 1;
  8269 					core->sound->playSfx("PowerUp");
  8270 					chargeEmitter->load("ChargeNature");
  8271 					chargeEmitter->start();
  8272 				}
  8273 
  8274 				if (state.spellCharge >= 2.0 && chargeLevelAttained<2)
  8275 				{
  8276 					chargeLevelAttained = 2;
  8277 					core->sound->playSfx("PowerUp");
  8278 					chargeEmitter->load("ChargeNature2");
  8279 					chargeEmitter->start();
  8280 
  8281 					chargingEmitter->load("ChargingNature2");
  8282 					chargingEmitter->start();
  8283 				}
  8284 				*/
  8285 			}
  8286 			break;
  8287 			}
  8288 		}
  8289 		/*
  8290 		float angle = 3.14f - ((rotation.z/180)*3.14f);
  8291 		int height = 25;
  8292 		*/
  8293 		//hair->hairNodes[0].position = position + Vector(sin(angle)*height, cos(angle)*height);
  8294 
  8295 
  8296 		if (biteTimer < biteTimerMax)
  8297 		{
  8298 			biteTimer += dt;
  8299 		}
  8300 		else
  8301 		{
  8302 			biteLeftEmitter.stop();
  8303 			biteRightEmitter.stop();
  8304 			biteTimer = biteTimerMax;
  8305 		}
  8306 
  8307 		if (biteTimer > biteTimerBiteRange)
  8308 		{
  8309 			biteLeftEmitter.stop();
  8310 			biteRightEmitter.stop();
  8311 		}
  8312 
  8313 		/*
  8314 		std::ostringstream os;
  8315 		os << "biteTimer: " << biteTimer;
  8316 		debugLog(os.str());
  8317 		*/
  8318 
  8319 		if (isInputEnabled())
  8320 		{
  8321 			if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "urchin") == 0)
  8322 			{
  8323 				if (!isEntityDead() && health > 0)
  8324 				{
  8325 					urchinDelay -= dt;
  8326 					if (urchinDelay < 0)
  8327 					{
  8328 						urchinDelay = 0.1;
  8329 
  8330 						Shot *s = dsq->game->fireShot("urchin", this, 0, position + offset);
  8331 					}
  8332 				}
  8333 			}
  8334 
  8335 			if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "jelly")==0)
  8336 			{
  8337 				if (!isEntityDead() && health > 0)
  8338 				{
  8339 					if (health < (maxHealth*JELLYCOSTUME_HEALTHPERC))
  8340 					{
  8341 						jellyDelay -= dt;
  8342 						if (jellyDelay < 0)
  8343 						{
  8344 							jellyDelay = JELLYCOSTUME_HEALDELAY; 
  8345 
  8346 							Vector d;
  8347 							if (!vel.isZero())
  8348 							{
  8349 								d = vel;
  8350 								d.setLength2D(16);
  8351 							}
  8352 
  8353 							dsq->game->spawnManaBall(position + offset + d, JELLYCOSTUME_HEALAMOUNT);
  8354 
  8355 							//Shot *s = dsq->game->fireShot("urchin", this, 0, getWorldPosition());
  8356 						}
  8357 					}
  8358 				}
  8359 			}
  8360 		}
  8361 
  8362 		if (dsq->continuity.form == FORM_BEAST && bone_head && biteTimer < biteTimerBiteRange && biteTimer > 0)
  8363 		{
  8364 			biteDelay -= dt;
  8365 			if (biteDelay < 0)
  8366 			{
  8367 				biteDelay = biteDelayPeriod;
  8368 
  8369 				Vector p = bone_head->getWorldPosition();
  8370 				std::string shot = "Bite";
  8371 				if (dsq->continuity.biteMult > 1)
  8372 				{
  8373 					shot = "SuperBite";
  8374 				}
  8375 				Shot *s = dsq->game->fireShot(shot, this, 0, p);
  8376 				//s->setAimVector(getNormal());
  8377 			}
  8378 		}
  8379 
  8380 		if (dsq->continuity.form == FORM_FISH && dsq->continuity.fishPoisonTimer.isActive())
  8381 		{
  8382 			if (!vel.isLength2DIn(16))
  8383 			{
  8384 				static float fishPoison = 0;
  8385 				fishPoison += dt;
  8386 				if (fishPoison > 0.2)
  8387 				{
  8388 					fishPoison = 0;
  8389 					Shot *s = dsq->game->fireShot("FishPoison", this, 0, position);
  8390 				}
  8391 			}
  8392 		}
  8393 
  8394 		if (!(state.lockedToWall || state.dodgeEffectTimer.isActive()) && _isUnderWater && dsq->continuity.getWorldType() == WT_NORMAL && canMove)
  8395 		{
  8396 			if (bursting)
  8397 			{
  8398 				//debugLog("bursting~!");
  8399 				burst -= dt * BURST_USE_RATE;
  8400 				burstTimer += dt;
  8401 
  8402 				/*
  8403 				std::ostringstream os;
  8404 				os << "burst: " << burst;
  8405 				debugLog(os.str());
  8406 				*/
  8407 				if (burst <= 0)
  8408 				{
  8409 					stopBurst();
  8410 				}
  8411 			}
  8412 
  8413 
  8414 		}
  8415 		if (inputEnabled && _isUnderWater)
  8416 		{
  8417 			if (bursting)
  8418 			{
  8419 				// disable check to stop burst
  8420 				/*
  8421 				if (!isActing("a1"))
  8422 				{
  8423 					stopBurst();
  8424 					//bursting = false;
  8425 					//burstDelay = BURST_DELAY;
  8426 					//animatedBurst = false;
  8427 				}
  8428 				*/
  8429 			}
  8430 			else if (burstDelay > 0)
  8431 			{
  8432 				burstDelay -= dt;
  8433 				if (burstDelay <= 0)
  8434 					burstDelay = 0;
  8435 			}
  8436 			else if (burst < 1)
  8437 			{
  8438 				burst += BURST_RECOVER_RATE * dt;
  8439 				if (burst >= 1)
  8440 					burst = 1;
  8441 			}
  8442 		}
  8443 
  8444 		bool moved = false;
  8445 
  8446 		//check to make sure there's still a wall there, if not fall off
  8447 		if (state.lockedToWall && !state.crawlingOnWall)
  8448 		{
  8449 			rotateToVec(wallPushVec, dt*2);
  8450 			if (!boneLock.on && !dsq->game->isObstructed(wallLockTile))
  8451 			{
  8452 				//debugLog("Dropping from wall");
  8453 				fallOffWall();
  8454 			}
  8455 		}
  8456 
  8457 		if (getState() != STATE_PUSH && !state.lockedToWall && inputEnabled && !ignoreInputDelay && _isUnderWater && canMove)
  8458 		{
  8459 			float a = 800*dt;
  8460 			Vector lastVel = vel;
  8461 			Vector addVec;
  8462 
  8463 			bool isMovingSlow = false;
  8464 			static Vector lastMousePos;
  8465 			Vector pos = lastMousePos - dsq->getGameCursorPosition();
  8466 			static bool lastDown;
  8467 			//int maxMouse = 200;
  8468 
  8469 			float len = 0;
  8470 			//dsq->continuity.toggleMoveMode &&
  8471 			//!dsq->continuity.toggleMoveMode &&
  8472 			
  8473 			if (isMiniMapCursorOkay() && !isActing(ACTION_ROLL) &&
  8474 				_isUnderWater && !riding && !boneLock.on &&
  8475 				(movingOn || ((dsq->inputMode == INPUT_JOYSTICK || dsq->inputMode== INPUT_KEYBOARD) || (core->mouse.buttons.left || bursting))))
  8476 			{
  8477 				//addVec = getVectorToCursorFr
  8478 				//(dsq->inputMode != INPUT_JOYSTICK && dsq->inputMode != INPUT_KEYBOARD)
  8479 				if (dsq->inputMode == INPUT_MOUSE || !this->singing)
  8480 				{
  8481 					addVec = getVectorToCursorFromScreenCentre();//getVectorToCursor();
  8482 
  8483 					if (dsq->inputMode == INPUT_MOUSE)
  8484 					{
  8485 						static Vector lastAddVec;
  8486 						if (!isActing(ACTION_PRIMARY) && bursting)
  8487 						{
  8488 							addVec = lastAddVec;
  8489 						}
  8490 
  8491 						if (bursting)
  8492 						{
  8493 							lastAddVec = addVec;
  8494 						}
  8495 					}
  8496 
  8497 					if (addVec.isLength2DIn(minMouse))
  8498 					{
  8499 						if (dsq->inputMode == INPUT_JOYSTICK)
  8500 							addVec = Vector(0,0,0);
  8501 						/*
  8502 						if (dsq->inputMode == INPUT_JOYSTICK && !core->mouse.buttons.left)
  8503 						{
  8504 							addVec = Vector(0,0,0);
  8505 						}
  8506 						*/
  8507 					}
  8508 
  8509 
  8510 
  8511 					/*
  8512 					if (!core->mouse.buttons.left && bursting)
  8513 					{
  8514 						addVec = vel;
  8515 					}
  8516 					*/
  8517 
  8518 					if (!addVec.isLength2DIn(minMouse))
  8519 					{
  8520 						//if (core->mouse.buttons.left)
  8521 						{
  8522 							len = addVec.getLength2D();
  8523 							if (len > 200)
  8524 								addVec.setLength2D(a *10);
  8525 							if (len > 100)
  8526 								addVec.setLength2D(a *2);
  8527 							else
  8528 								addVec.setLength2D(a);
  8529 
  8530 							addVec *= dsq->continuity.speedMult;
  8531 
  8532 							//128
  8533 							if (len < maxMouse && !bursting)
  8534 							{
  8535 								isMovingSlow = true;
  8536 							}
  8537 						}
  8538 					}
  8539 					else
  8540 					{
  8541 						// stop movement
  8542 						if (addVec.isLength2DIn(STOP_DISTANCE))
  8543 						{
  8544 							vel *= 0.9;
  8545 							if (!rolling)
  8546 								rotation.interpolateTo(Vector(0,0,0), 0.1);
  8547 							if (vel.isLength2DIn(50))
  8548 							{
  8549 								if (bursting)
  8550 								{
  8551 									stopBurst();
  8552 								}
  8553 							}
  8554 							//vel = Vector(0,0,0);
  8555 						}
  8556 						addVec = Vector(0,0,0);
  8557 					}
  8558 				}
  8559 
  8560 				//addVec |= a;
  8561 				/*
  8562 				if (pos.getSquaredLength2D() > 10000)
  8563 				{
  8564 					startBurst();
  8565 				}
  8566 				*/
  8567 			}
  8568 			else
  8569 			{
  8570 			}
  8571 			lastDown = core->mouse.buttons.left;
  8572 
  8573 			/*
  8574 			std::ostringstream os;
  8575 			os << "addVec(" << addVec.x << ", " << addVec.y << ")";
  8576 			debugLog(os.str());
  8577 			*/
  8578 			lastMousePos = dsq->getGameCursorPosition();
  8579 
  8580 			if (!rolling && !state.backFlip && !flourish)
  8581 			{
  8582 				bool swimOnBack = false;
  8583 				if (swimOnBack)
  8584 				{
  8585 					if (addVec.x > 0)
  8586 					{
  8587 						if (isfh())
  8588 							flipHorizontal();
  8589 					}
  8590 					if (addVec.x < 0)
  8591 					{
  8592 						if (!isfh())
  8593 							flipHorizontal();
  8594 					}
  8595 				}
  8596 				else
  8597 				{
  8598 					if (addVec.x > 0)
  8599 					{
  8600 						if (!isfh())
  8601 							flipHorizontal();
  8602 					}
  8603 					if (addVec.x < 0)
  8604 					{
  8605 						if (isfh())
  8606 							flipHorizontal();
  8607 					}
  8608 				}
  8609 			}
  8610 
  8611 
  8612 			/*
  8613 			// HACK: joystick code / slow
  8614 			if (addVec.x == 0 && addVec.y == 0)
  8615 			{
  8616 				float jpos[2];
  8617 				glfwGetJoystickPos(GLFW_JOYSTICK_1, jpos, 2);
  8618 				const float deadZone = 0.1;
  8619 				if (fabs(jpos[0]) > deadZone || fabs(jpos[1]) > deadZone)
  8620 					addVec = Vector(jpos[0]*a, -jpos[1]*a);
  8621 			}
  8622 			*/
  8623 
  8624 
  8625 			// will not get here if not underwater
  8626 			if (isLockable())
  8627 				lockToWall();
  8628 			if ((addVec.x != 0 || addVec.y != 0))
  8629 			{
  8630 				currentMaxSpeed=0;
  8631 				vel += addVec;
  8632 				//addVec |= a;
  8633 				//float cheatLen = vel.getSquaredLength2D();
  8634 				if (bursting)
  8635 				{
  8636 					Vector add = addVec;
  8637 					/*
  8638 					// HACK: this will let the player boost in one direction while turning to face another
  8639 					if (!core->mouse.buttons.left)
  8640 					{
  8641 						add = vel;
  8642 					}
  8643 					*/
  8644 					add.setLength2D(BURST_ACCEL*dt);
  8645 					vel += add;
  8646 
  8647 					if (pushingOffWallEffect > 0 || wallJumps > 0)
  8648 						currentMaxSpeed = vars->maxWallJumpBurstSpeed + 50*wallJumps;
  8649 					else
  8650 						currentMaxSpeed = vars->maxBurstSpeed;
  8651 
  8652 				}
  8653 				else
  8654 				{
  8655 					if (pushingOffWallEffect > 0)
  8656 						currentMaxSpeed = vars->maxWallJumpSpeed;
  8657 					else if (state.dodgeEffectTimer.isActive())
  8658 						currentMaxSpeed = vars->maxDodgeSpeed;
  8659 					else
  8660 					{
  8661 						if (isActing(ACTION_SLOW) || isMovingSlow)
  8662 						{
  8663 							/*
  8664 							int spdRange = maxMouse - minMouse;
  8665 							float p = (len - minMouse) / spdRange;
  8666 							int spd = p * vars->maxSwimSpeed;// + minMouse
  8667 							currentMaxSpeed = spd;
  8668 							*/
  8669 							currentMaxSpeed = vars->maxSlowSwimSpeed;
  8670 						}
  8671 						//else if (dsq->continuity.getWorldType() == WT_NORMAL)
  8672 						else
  8673 							currentMaxSpeed = vars->maxSwimSpeed;
  8674 						/*
  8675 						else
  8676 							currentMaxSpeed = vars->maxDreamWorldSpeed;
  8677 						*/
  8678 					}
  8679 				}
  8680 
  8681 				/*
  8682 				if (dsq->continuity.form == FORM_SPIRIT)
  8683 					currentMaxSpeed *= 0.5;
  8684 				*/
  8685 
  8686 				if (leaches > 0)
  8687 				{
  8688 					currentMaxSpeed -= leaches*60;
  8689 				//	vel |= vel.getLength2D()-1*leaches;
  8690 				}
  8691 
  8692 				if (state.blind)
  8693 					currentMaxSpeed -= 100;
  8694 
  8695 				if (currentMaxSpeed < 0)
  8696 					currentMaxSpeed = 1;
  8697 
  8698 				if (ropeState == 2 && currentMaxSpeed < vars->maxWallJumpBurstSpeed)
  8699 					currentMaxSpeed = vars->maxWallJumpBurstSpeed;
  8700 
  8701 				/*
  8702 				if (inCurrent)
  8703 				{
  8704 					ropeState = 0;
  8705 					currentMaxSpeed = 1200;
  8706 				}
  8707 				*/
  8708 
  8709 				//clampVelocity();
  8710 
  8711 
  8712 				//float angle;
  8713 
  8714 				if (getState() == STATE_TRANSFORM)
  8715 					rotateToVec(addVec, 0.1, 90);
  8716 				else
  8717 				{
  8718 					if (rolling)
  8719 					{
  8720 						// here for roll key?
  8721 						// seems like this isn't reached
  8722 						//if (isActing("roll"))
  8723 						if (isActing(ACTION_ROLL))
  8724 						{
  8725 							//debugLog("here");
  8726 						}
  8727 						else
  8728 						{
  8729 							float t = 0;
  8730 							if (dsq->inputMode == INPUT_KEYBOARD)
  8731 								t = 0.1;
  8732 							rotateToVec(addVec, t);
  8733 						}
  8734 					}
  8735 					else if (bursting && flourish)
  8736 					{
  8737 
  8738 					}
  8739 					else
  8740 					{
  8741 						/*
  8742 						if (bursting && !core->mouse.buttons.left)
  8743 						{
  8744 						}
  8745 						else
  8746 							rotateToVec(addVec, 0.1);
  8747 						*/
  8748 						if (!state.nearWall && !flourish)
  8749 							rotateToVec(addVec, 0.1);
  8750 					}
  8751 				}
  8752 
  8753 				moved = true;
  8754 				if ((!swimming || (swimming && !bursting && skeletalSprite.getCurrentAnimation()->name != "swim")) && !state.lockedToWall)
  8755 				{
  8756 					swimming = true;
  8757 					//Animation *a = skeletalSprite.getCurrentAnimation();
  8758 
  8759 					if (getState() == STATE_IDLE && !rolling)
  8760 					{
  8761 						skeletalSprite.transitionAnimate("swim", ANIM_TRANSITION, -1);
  8762 					}
  8763 						//animate(anim_swim);
  8764 				}
  8765 				skeletalSprite.setTimeMultiplier(1);
  8766 				Animation *anim=skeletalSprite.getCurrentAnimation();
  8767 				if (!bursting && (anim && anim->name == "swim"))
  8768 				{
  8769 					float velLen = vel.getLength2D();
  8770 					float time = velLen / 1200.0f;
  8771 					if (velLen > 1200)
  8772 						time = 1;
  8773 					//skeletalSprite.setTimeMultiplier(time*3);// 5
  8774 					//skeletalSprite.setTimeMultiplier(time*3.5);
  8775 					//skeletalSprite.setTimeMultiplier(time*4);
  8776 					//animator.timePeriod = 1.5*(1.0f-time);
  8777 					skeletalSprite.setTimeMultiplier(time*4.5);
  8778 				}
  8779 				else
  8780 				{
  8781 					if (currentAnim != getBurstAnimName() && skeletalSprite.getCurrentAnimation()->name != getBurstAnimName() && !state.lockedToWall)
  8782 					{
  8783 						if (getState() == STATE_IDLE && !rolling)
  8784 							skeletalSprite.transitionAnimate(getBurstAnimName(), ANIM_TRANSITION);
  8785 							//animate(anim_burst);
  8786 						animatedBurst = true;
  8787 					}
  8788 				}
  8789 			}
  8790 		}
  8791 
  8792 		int currentSwimSpeed = 400;
  8793 		//if (dsq->continuity.getWorldType() == WT_SPIRIT)
  8794 		/*
  8795 		if (dsq->continuity.form == FORM_SPIRIT)
  8796 		{
  8797 			currentSwimSpeed *= 0.3;
  8798 		}
  8799 		*/
  8800 		if (!_isUnderWater && !state.lockedToWall)
  8801 		{
  8802 			//currentSwimSpeed *= 1.5;
  8803 			//float a = *dt;
  8804 			// base on where the mouse is
  8805 			/*
  8806 			Vector addVec;
  8807 			addVec = getVectorToCursorFromScreenCentre();
  8808 			addVec.setLength2D(a);
  8809 			*/
  8810 
  8811 			// gravity
  8812 			float fallMod = 1.5;
  8813 			if (dsq->continuity.form == FORM_SPIRIT)
  8814 			{
  8815 				fallMod = 1.0;
  8816 			}
  8817 			vel += Vector(0,980)*dt*fallMod;
  8818 
  8819 			if (!rolling && !state.backFlip && !flourish)
  8820 			{
  8821 				if (vel.x != 0 || vel.y != 0)
  8822 					rotateToVec(vel, 0.1);
  8823 
  8824 				if (vel.x > 0)
  8825 				{
  8826 					if (!isfh())
  8827 						flipHorizontal();
  8828 				}
  8829 				if (vel.x < 0)
  8830 				{
  8831 					if (isfh())
  8832 						flipHorizontal();
  8833 				}
  8834 			}
  8835 			if (rolling && !state.backFlip)
  8836 			{
  8837 				Vector v = getVectorToCursorFromScreenCentre();
  8838 				rotateToVec(v, 0.01);
  8839 			}
  8840 			if (isLockable())
  8841 				lockToWall();
  8842 			/*
  8843 			if (isActing("left"))
  8844 				addVec += Vector(-a, 0);
  8845 			if (isActing("right"))
  8846 				addVec += Vector(a, 0);
  8847 			*/
  8848 			/*
  8849 			if (isActing("up"))
  8850 				addVec += Vector(0, -a);
  8851 			if (isActing("down"))
  8852 				addVec += Vector(0, a);
  8853 			*/
  8854 			//vel += addVec;
  8855 		}
  8856 
  8857 		if (!moved)
  8858 		{
  8859 			if (swimming)
  8860 			{
  8861 				swimming = false;
  8862 				idleAnimDelay = 0;
  8863 				if (dsq->continuity.form == FORM_FISH)
  8864 					rotation.interpolateTo(0, 0.2);
  8865 			}
  8866 			// "friction"
  8867 			//vel += -vel*0.999f*dt;
  8868 			if (_isUnderWater)
  8869 			{
  8870 				/*
  8871 				std::ostringstream os;
  8872 				os << "fric(" << vel.x << ", " << vel.y;
  8873 				debugLog(os.str());
  8874 				*/
  8875 				if (isInCurrent())
  8876 					doFriction(dt*5);
  8877 				else
  8878 					doFriction(dt);
  8879 			}
  8880 		}
  8881 
  8882 	}
  8883 
  8884 	if (_isUnderWater && isInCurrent())
  8885 	{
  8886 		if (dsq->loops.current == BBGE_AUDIO_NOCHANNEL)
  8887 		{
  8888 			PlaySfx play;
  8889 			play.name = "CurrentLoop";
  8890 			play.vol = 1;
  8891 			play.time = 1;
  8892 			play.fade = SFT_IN;
  8893 			play.loops = -1;
  8894 			dsq->loops.current = core->sound->playSfx(play);
  8895 		}
  8896 	}
  8897 	else
  8898 	{
  8899 		if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
  8900 		{
  8901 			core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
  8902 			dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
  8903 		}
  8904 	}
  8905 
  8906 	if (!swimming && _isUnderWater)
  8907 	{
  8908 		if (!inCurrent)
  8909 			currentMaxSpeed = vars->maxSwimSpeed;
  8910 
  8911 		if (ropeState == 2 && currentMaxSpeed < vars->maxWallJumpBurstSpeed)
  8912 			currentMaxSpeed = vars->maxWallJumpBurstSpeed;
  8913 
  8914 		if (!state.lockedToWall && !bursting)
  8915 		{
  8916 			if (getState() == STATE_IDLE && inputEnabled)
  8917 			{
  8918 				Animation *a = skeletalSprite.getCurrentAnimation(0);
  8919 				if (a && a->name != getIdleAnimName() && a->name != "pushed" && a->name != "spin" && !rolling)
  8920 					skeletalSprite.transitionAnimate(getIdleAnimName(), ANIM_TRANSITION, -1);
  8921 			}
  8922 			/*
  8923 			idleAnimDelay -= dt;
  8924 			if (idleAnimDelay <= 0)
  8925 			{
  8926 				idleAnimDelay = 1.5;/*anim_idle.time*2;*/
  8927 
  8928 				/*
  8929 				if (currentAction == IDLE && (!skeletalSprite.isAnimating() || skeletalSprite.getCurrentAnimation()->name=="swim"
  8930 					|| skeletalSprite.getCurrentAnimation()->name=="a1"))
  8931 				*/
  8932 			/*
  8933 				if (currentAction == STATE_IDLE)
  8934 				{
  8935 					skeletalSprite.transitionAnimate("idle", ANIM_TRANSITION);
  8936 				}
  8937 
  8938 					//animate(anim_idle);
  8939 			}
  8940 			*/
  8941 		}
  8942 	}
  8943 
  8944 	if (_isUnderWater && fallGravityTimer)
  8945 	{
  8946 		fallGravityTimer -= dt;
  8947 		currentMaxSpeed = lastOutOfWaterMaxSpeed;
  8948 		if (fallGravityTimer < 0)
  8949 			fallGravityTimer = 0;
  8950 	}
  8951 
  8952 
  8953 
  8954 	clampVelocity();
  8955 
  8956 	if (swimming)
  8957 	{
  8958 		//static bool lastSpreadUp = false;
  8959 		if (!rolling && !internalOffset.isInterpolating())
  8960 		{
  8961 			int spread = 8, rotSpread = 45;
  8962 			float t = 1;
  8963 
  8964 			internalOffset = Vector(-spread, 0);
  8965 			internalOffset.interpolateTo(Vector(spread, 0), t, -1, 1, 1);
  8966 
  8967 			/*
  8968 			rotationOffset = Vector(-rotSpread, 0);
  8969 			rotationOffset.interpolateTo(Vector(rotSpread, 0), t, -1, 1, 1);
  8970 			*/
  8971 
  8972 			for (int i = 0; i < int((t*0.5)/0.01); i++)
  8973 			{
  8974 				internalOffset.update(0.01);
  8975 				//rotationOffset.update(0.01);
  8976 			}
  8977 			/*
  8978 			if (lastSpreadUp)
  8979 				internalOffset.interpolateTo(Vector(spread, 0), t, 1, 1, 1);
  8980 			else
  8981 				internalOffset.interpolateTo(Vector(-spread, 0), t, 1, 1, 1);
  8982 			*/
  8983 
  8984 			//lastSpreadUp = !lastSpreadUp;
  8985 			//internalOffset.update(t*0.5);
  8986 		}
  8987 
  8988 		if (dsq->continuity.form != FORM_ENERGY && dsq->continuity.form != FORM_DUAL && dsq->continuity.form != FORM_FISH)
  8989 		{
  8990 			if (leaches <= 0 && !bursting && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
  8991 			{
  8992 				state.swimTimer += dt;
  8993 
  8994 				if (state.swimTimer > 5)
  8995 				{
  8996 					state.swimTimer = 0 - rand()%3;
  8997 					static int lastSwimExtra = -1;
  8998 					int maxAnim = 4;
  8999 					int anim = rand()%maxAnim;
  9000 					if (anim == lastSwimExtra)
  9001 						anim++;
  9002 					if (anim >= maxAnim)
  9003 						anim = 0;
  9004 					lastSwimExtra = anim;
  9005 					anim ++;
  9006 
  9007 					std::ostringstream os;
  9008 					os << "swimExtra-" << anim;
  9009 					skeletalSprite.transitionAnimate(os.str(), 0.5, 0, 6);
  9010 				}
  9011 			}
  9012 		}
  9013 	}
  9014 	else
  9015 	{
  9016 
  9017 	}
  9018 
  9019 	if (!swimming || rolling)
  9020 	{
  9021 		//state.swimTimer = 0;
  9022 		if (skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
  9023 		{
  9024 			skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
  9025 		}
  9026 		internalOffset.interpolateTo(Vector(0,0),0.5);
  9027 	}
  9028 
  9029 	checkNearWall();
  9030 	//if (core->getNestedMains()==1)
  9031 	{
  9032 		Vector zoomSurface(0.55, 0.55);
  9033 		Vector zoomMove(vars->zoomMove, vars->zoomMove), zoomStop(vars->zoomStop, vars->zoomStop), zoomNaija(vars->zoomNaija, vars->zoomNaija);
  9034 		float cheatLen = getMoveVel().getSquaredLength2D();
  9035 
  9036 		if (cheatLen > sqr(250) && _isUnderWater && !state.lockedToWall)
  9037 		{
  9038 			if (!swimEmitter.isRunning())
  9039 				swimEmitter.start();
  9040 		}
  9041 		else
  9042 		{
  9043 			swimEmitter.stop();
  9044 		}
  9045 
  9046 		Vector targetScale(1,1);
  9047 		float zoomVel = 0;
  9048 		if (zoomOverriden || isEntityDead() || core->globalScale.isInterpolating())
  9049 		{
  9050 
  9051 		}
  9052 		else
  9053 		{
  9054 			if (dsq->game->waterLevel.x > 0 && fabs(avatar->position.y - dsq->game->waterLevel.x) < 800)
  9055 			{
  9056 				float time = 0.5;
  9057 				if (!myZoom.interpolating || (core->globalScale.target != zoomSurface && myZoom.timePeriod != time))
  9058 				{
  9059 					myZoom.interpolateTo(zoomSurface, time, 0, 0, 1);
  9060 				}
  9061 			}
  9062 			else if (avatar->looking == 2)
  9063 			{
  9064 				float time = 1.0;
  9065 				if (!myZoom.interpolating || (core->globalScale.target != zoomNaija && myZoom.timePeriod != time))
  9066 				{
  9067 					/*
  9068 					std::ostringstream os;
  9069 					os << "zooming in on Naija: " << zoomNaija.x;
  9070 					debugLog(os.str());
  9071 					*/
  9072 					myZoom.interpolateTo(zoomNaija, time, 0, 0, 1);
  9073 				}
  9074 			}
  9075 			else if ((cheatLen > sqr(250) && cheatLen < sqr(1000)) || attachedTo || avatar->looking==1)
  9076 			{
  9077 				float time = 3;
  9078 				if (avatar->looking)
  9079 				{
  9080 					time = 1.0;
  9081 				}
  9082 				if (!myZoom.interpolating || (core->globalScale.target != zoomMove && myZoom.timePeriod != time))
  9083 					myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
  9084 			}
  9085 			else if (cheatLen < sqr(210) && !state.lockedToWall && stillTimer.getValue() > 4 && !isSinging())
  9086 			{
  9087 				float time = 10;
  9088 				if (!myZoom.interpolating || (myZoom.target != zoomStop && myZoom.timePeriod != time))
  9089 					myZoom.interpolateTo(zoomStop, time, 0, 0, 1);
  9090 			}
  9091 			else if (cheatLen >= sqr(1000))
  9092 			{
  9093 				float time = 1.6;
  9094 				if (!myZoom.interpolating || (myZoom.target != zoomMove && myZoom.timePeriod != time))
  9095 					myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
  9096 			}
  9097 
  9098 			if (myZoom.x < game->maxZoom)
  9099 			{
  9100 				core->globalScale.x = game->maxZoom;
  9101 				core->globalScale.y = game->maxZoom;
  9102 			}
  9103 			else
  9104 			{
  9105 				core->globalScale.x = myZoom.x;
  9106 				core->globalScale.y = myZoom.y;
  9107 			}
  9108 
  9109 		}
  9110 
  9111 		if (state.dodgeEffectTimer.isActive())
  9112 		{
  9113 			vel += dodgeVec*dt;
  9114 		}
  9115 
  9116 		if (!state.lockedToWall && !bursting && _isUnderWater && swimming && !isFollowingPath())
  9117 		{
  9118 			//debugLog("collision avoidance");
  9119 			if (dsq->continuity.form == FORM_FISH)
  9120 				doCollisionAvoidance(dt, 1, 0.1, 0, 800, OT_HURT);
  9121 			else
  9122 				doCollisionAvoidance(dt, 2, 1.0, 0, 800, OT_HURT);
  9123 		}
  9124 
  9125 		// friction for extraVel
  9126 		if (!extraVel.isZero())
  9127 		{
  9128 			Vector d = extraVel;
  9129 			d.setLength2D(100);
  9130 			extraVel -= d*dt;
  9131 		}
  9132 
  9133 
  9134 		if (!game->isShuttingDownGameState())
  9135 		{
  9136 			updateCurrents(dt);
  9137 			updateVel2(dt);
  9138 		}
  9139 		else
  9140 		{
  9141 			vel2 = Vector(0,0,0);
  9142 		}
  9143 
  9144 		//int collideCircle = 24;//24; // 48
  9145 		int collideCircle = 10;
  9146 		if (dsq->continuity.form == FORM_FISH)
  9147 			collideCircle = 8;
  9148 		// just for external access
  9149 		// HACK: should always be using collide radius :| ?
  9150 
  9151 		//updateMovement
  9152 		collideRadius = collideCircle;
  9153 		if (!state.lockedToWall && !isFollowingPath() && !riding)
  9154 		{
  9155 /*collideCheck:*/
  9156 
  9157 			if (vel.getLength2D() < sqr(2))
  9158 			{
  9159 				vel = Vector(0,0,0);
  9160 			}
  9161 			Vector moveVel;
  9162 			if (!isInputEnabled() && isv(EV_NOINPUTNOVEL, 1))
  9163 			{
  9164 				vel2=vel=Vector(0,0);
  9165 			}
  9166 			else
  9167 			{
  9168 				moveVel = getMoveVel();
  9169 			}
  9170 			if (!moveVel.isZero())
  9171 			{
  9172 				bool collided = false;
  9173 
  9174 				/*
  9175 				std::ostringstream os;
  9176 				os << "vel (" << vel.x << ", " << vel.y << ")";
  9177 				debugLog(os.str());
  9178 				*/
  9179 				Vector mov = (moveVel * dt) + (extraVel * dt);
  9180 				Vector omov = mov;
  9181 				mov.capLength2D(TILE_SIZE);
  9182 				/*
  9183 				if (mov.getSquaredLength2D() > sqr(TILE_SIZE))
  9184 					mov.setLength2D(TILE_SIZE);
  9185 				*/
  9186 				if (omov.getSquaredLength2D() > 0)
  9187 				{
  9188 					while (omov.getSquaredLength2D() > 0)
  9189 					{
  9190 						if (omov.getSquaredLength2D() < sqr(TILE_SIZE))
  9191 						{
  9192 							mov = omov;
  9193 							omov = Vector(0,0);
  9194 						}
  9195 						else
  9196 							omov -= mov;
  9197 
  9198 						lastLastPosition = position;
  9199 						lastPosition = position;
  9200 						Vector newPosition = position + mov;
  9201 						//Vector testPosition = position + (vel *dt)*2;
  9202 						position = newPosition;
  9203 
  9204 
  9205 						int hw = collideCircle;
  9206 						Vector fix;
  9207 
  9208 						if (dsq->game->collideCircleWithGrid(position, hw, &fix))
  9209 						{
  9210 							if (dsq->game->lastCollideTileType == OT_HURT
  9211 								&& dsq->continuity.getWorldType() != WT_SPIRIT
  9212 								&& dsq->continuity.form != FORM_NATURE)
  9213 							{
  9214 								DamageData d;
  9215 								d.damage = 1;
  9216 								damage(d);
  9217 								vel2 = Vector(0,0,0);
  9218 								//doCollisionAvoidance(1, 3, 1);
  9219 								/*
  9220 								Vector v = dsq->game->getWallNormal(position);
  9221 								if (!v.isZero())
  9222 								{
  9223 									vel += v * 500;
  9224 								}
  9225 								*/
  9226 							}
  9227 							collided = true;
  9228 
  9229 							if (currentState == STATE_PUSH)
  9230 							{
  9231 								dsq->sound->playSfx("rockhit");
  9232 								dsq->spawnParticleEffect("rockhit", position);
  9233 								if (pushDamage)
  9234 								{
  9235 									DamageData d;
  9236 									d.damage = pushDamage;
  9237 									damage(d);
  9238 								}
  9239 								setState(STATE_IDLE);
  9240 							}
  9241 
  9242 							if (!_isUnderWater)
  9243 							{
  9244 								/*
  9245 								doBounce();
  9246 								position = lastPosition;
  9247 								*/
  9248 
  9249 								// this is the out of water bounce...
  9250 
  9251 								//debugLog("above water bounce");
  9252 
  9253 								
  9254 
  9255 								Vector n = getWallNormal(TileVector(lastPosition));
  9256 								n *= vel.getLength2D();
  9257 
  9258 								/*
  9259 								std::ostringstream os;
  9260 								os << "vel(" << vel.x << ", " << vel.y << ") n(" << n.x << ", " << n.y << ")";
  9261 								debugLog(os.str());
  9262 								*/
  9263 
  9264 								//flopping = true;
  9265 
  9266 								vel = -vel;
  9267 
  9268 								//vel = (vel*0.5 + n*0.5);
  9269 								if (vel.y < 0)
  9270 								{
  9271 									//debugLog("Vel less than 0");
  9272 									
  9273 								}
  9274 								if (vel.y == 0)
  9275 								{
  9276 									//debugLog("Vel was 0");
  9277 									vel = getWallNormal(TileVector(position));
  9278 									vel.setLength2D(500);
  9279 								}
  9280 
  9281 								if (vel.isLength2DIn(500))
  9282 									vel.setLength2D(500);
  9283 								
  9284 
  9285 								vel.capLength2D(800);
  9286 
  9287 								position = lastPosition;
  9288 								this->doCollisionAvoidance(1, 4, 0.5, 0, 500);
  9289 
  9290 								
  9291 								/*
  9292 								vel = -vel;
  9293 								if (vel.y < 0)
  9294 									vel.y *= 2;
  9295 								position = lastPosition;
  9296 								*/
  9297 							}
  9298 							else
  9299 							{
  9300 								float len = vel.getLength2D();
  9301 								position = lastPosition;
  9302 
  9303 								// works as long as not buried in wall ... yep. =(
  9304 								if (dsq->game->isObstructed(TileVector(position)))
  9305 								{
  9306 									//vel = -vel*0.5;
  9307 									vel = 0;
  9308 								}
  9309 								else
  9310 								{
  9311 									// && dsq->game->getPercObsInArea(position, 4) < 0.75
  9312 									if (!inCurrent)
  9313 									{
  9314 										/*
  9315 										int px=int(position.x);
  9316 										int py=int(position.y);
  9317 										int llpx=int(lastLastPosition.x);
  9318 										int llpy=int(lastLastPosition.y);
  9319 										if (px == llpx && py == llpy)
  9320 										{
  9321 
  9322 										}
  9323 										else
  9324 										{
  9325 										*/
  9326 											//doBounce();
  9327 										if (bursting)
  9328 										{
  9329 											//vel = 0;
  9330 										}
  9331 										else
  9332 										{
  9333 											if (!_isUnderWater && dsq->continuity.form == FORM_FISH)
  9334 											{
  9335 												vel = 0;
  9336 											}
  9337 											else
  9338 											{
  9339 												// this bounce is what causes naija to get wedged
  9340 												float len = vel.getLength2D();
  9341 												Vector n = dsq->game->getWallNormal(position, 5);
  9342 												if (n.x == 0 && n.y == 0)
  9343 												{
  9344 													vel = 0;
  9345 												}
  9346 												else
  9347 												{
  9348 													//n.setLength2D(len/2);
  9349 													//vel += n;
  9350 													n.setLength2D(len);
  9351 													vel = (n+vel)*0.5;
  9352 													//vel = (n + vel)*0.5;
  9353 												}
  9354 											}
  9355 										//}
  9356 										}
  9357 									}
  9358 								}
  9359 
  9360 							}
  9361 						}
  9362 
  9363 
  9364 						if (!collided && checkWarpAreas()) collided = true;
  9365 						if (collided) break;
  9366 
  9367 						// DO NOT COLLIDE WITH LR_ENTITIES
  9368 						// ENTITY SCRIPTS WILL HANDLE Entity V. Entity COLLISIONS
  9369 
  9370 					}
  9371 				}
  9372 			}
  9373 		}
  9374 
  9375 		/*
  9376 		if (swimming)
  9377 		{
  9378 			int px=int(position.x);
  9379 			int py=int(position.y);
  9380 			int llpx=int(lastLastPosition.x);
  9381 			int llpy=int(lastLastPosition.y);
  9382 			if (px == llpx && py == llpy)
  9383 			{
  9384 				if (isNearObstruction(4))
  9385 				{
  9386 					vel = 0;
  9387 					Vector n = dsq->game->getWallNormal(position, 6);
  9388 					if (!n.isZero())
  9389 					{
  9390 						Vector add = n * 100;
  9391 						Vector f = getForward();
  9392 						n = (n + f * 100);
  9393 						n *= 0.5;
  9394 						vel += n;
  9395 					}
  9396 				}
  9397 			}
  9398 		}
  9399 		*/
  9400 
  9401 		if (ignoreInputDelay>0)
  9402 		{
  9403 			ignoreInputDelay -= dt;
  9404 			if (ignoreInputDelay < 0)
  9405 				ignoreInputDelay = 0;
  9406 		}
  9407 
  9408 		if (burstBar && burstBar->alpha == 1)
  9409 		{
  9410 			float amount = burst;
  9411 			if (amount > 1) amount = 1;
  9412 			if (amount < 0) amount = 0;
  9413 			burstBar->frame = (19-(amount*19));
  9414 		}
  9415 		//checkConvAreas();
  9416 		//checkSpecial();
  9417 	}
  9418 
  9419 	//fuuugly
  9420 	// you said it!
  9421 	if (riding || boneLock.on)
  9422 	{
  9423 		checkWarpAreas();
  9424 	}
  9425 	applyRidingPosition();
  9426 	updateHair(dt);
  9427 	chargeEmitter->position = chargingEmitter->position = position + offset;
  9428 
  9429 	// particle sets
  9430 	if (leftHandEmitter && rightHandEmitter && boneLeftHand && boneRightHand)
  9431 	{
  9432 		leftHandEmitter->position = boneLeftHand->getWorldCollidePosition(Vector(0, 16));
  9433 		rightHandEmitter->position = boneRightHand->getWorldCollidePosition(Vector(0,16));
  9434 	}
  9435 
  9436 	dsq->game->handleShotCollisions(this, (activeAura == AURA_SHIELD));
  9437 }
  9438 
  9439 
  9440 void Avatar::checkNearWall()
  9441 {
  9442 	state.nearWall = false;
  9443 
  9444 	if (!inCurrent && bursting && !state.lockedToWall && !vel.isZero() && !riding && _isUnderWater)
  9445 	{
  9446 		/*
  9447 		int mult = 1;
  9448 		if (isfh())
  9449 			mult = -1;
  9450 		*/
  9451 		/*
  9452 		Vector n = dsq->game->getWallNormal(position, 8);
  9453 		if (!n.isZero())
  9454 		{
  9455 			state.nearWall = true;
  9456 			float t=0.2;
  9457 			rotateToVec(n, t, 0);
  9458 			skeletalSprite.transitionAnimate("wall", t);
  9459 		}
  9460 		else
  9461 			state.nearWall = false;
  9462 		*/
  9463 		int checkRange = 11;
  9464 		Vector v = vel;
  9465 		v.normalize2D();
  9466 		TileVector oT(position);
  9467 		TileVector t=oT,lastT=oT;
  9468 		bool obs = false;
  9469 		for (int i = 1; i < checkRange; i++)
  9470 		{
  9471 			t.x = oT.x + v.x*i;
  9472 			t.y = oT.y + v.y*i;
  9473 			if (dsq->game->isObstructed(t) && dsq->game->getGrid(t) != OT_HURT)
  9474 			{
  9475 				obs = true;
  9476 				break;
  9477 			}
  9478 			lastT = t;
  9479 		}
  9480 		if (obs)
  9481 		{
  9482 			Vector n = dsq->game->getWallNormal(t.worldVector());
  9483 			if (!n.isZero())
  9484 			{
  9485 				state.nearWall = true;
  9486 				float t=0.2;
  9487 				rotateToVec(n, t, 0);
  9488 				skeletalSprite.transitionAnimate("wall", t);
  9489 			}
  9490 			else
  9491 				state.nearWall = false;
  9492 		}
  9493 		else
  9494 		{
  9495 			state.nearWall = false;
  9496 		}
  9497 	}
  9498 }
  9499 
  9500 void Avatar::checkSpecial()
  9501 {
  9502 	/*
  9503 	if (dsq->continuity.getFlag("VedhaFollow1") == 7)
  9504 	{
  9505 		int total = 0, c = 0;
  9506 		for (int i = 0; i < dsq->entities.size(); i++)
  9507 		{
  9508 			Entity *e = dsq->entities[i];
  9509 			if (e->name == "PracticeEnemy")
  9510 			{
  9511 				total++;
  9512 				if (e->isEntityDead())
  9513 					c++;
  9514 			}
  9515 		}
  9516 		if (total == c)
  9517 		{
  9518 			health = maxHealth;
  9519 			dsq->continuity.setFlag("VedhaFollow1", 8);
  9520 			dsq->getEntityByName("Vedha")->activate();
  9521 		}
  9522 	}
  9523 	*/
  9524 }
  9525 
  9526 void Avatar::checkConvAreas()
  9527 {
  9528 	if (core->getNestedMains() != 1) return;
  9529 	if (game->avatar->disableConversationStart) return;
  9530 	/*
  9531 	for (int i = 0; i < dsq->game->convAreas.size(); i++)
  9532 	{
  9533 		ConvArea * a = &dsq->game->convAreas[i];
  9534 		Vector diff = a->position - this->position;
  9535 		if (diff.getSquaredLength2D() < sqr(a->radius))
  9536 		{
  9537 			if (dsq->continuity.getFlag(a->conv)==0)
  9538 			{
  9539 				dsq->runScript(a->conv);
  9540 				return;
  9541 			}
  9542 		}
  9543 	}
  9544 	*/
  9545 	Entity *entityRun = 0;
  9546 	FOR_ENTITIES(i)
  9547 	{
  9548 		Entity *e = *i;
  9549 		if (e->activationType == Entity::ACT_RANGE)
  9550 		{
  9551 			Vector diff = e->position - this->position;
  9552 
  9553 			if (
  9554 				(e->canTalkWhileMoving || !e->position.isInterpolating())
  9555 				&& e->activationType == Entity::ACT_RANGE
  9556 				&& diff.getSquaredLength2D() < sqr(e->activationRange)
  9557 				)
  9558 			{
  9559 				entityRun = e;
  9560 				if (lastEntityActivation != e)
  9561 					e->activate();
  9562 
  9563 
  9564 				//dsq->runScript(e->convo);
  9565 				/*
  9566 				Vector push = diff;
  9567 				int range = e->activationRange + 40;
  9568 				if (diff.getSquaredLength2D() < sqr(range))
  9569 				{
  9570 					push |= range - diff.getLength2D();
  9571 					dsq->game->avatar->vel = -push*2;
  9572 				}
  9573 				*/
  9574 				break;
  9575 			}
  9576 		}
  9577 	}
  9578 	lastEntityActivation = entityRun;
  9579 }
  9580 
  9581 void Avatar::onWarp()
  9582 {
  9583 	avatar->setv(EV_NOINPUTNOVEL, 0);
  9584 	closeSingingInterface();
  9585 }
  9586 
  9587 bool Avatar::checkWarpAreas()
  9588 {
  9589 	int i = 0;
  9590 	for (i = 0; i < dsq->game->paths.size(); i++)
  9591 	{
  9592 		bool warp = false;
  9593 		Path *p = dsq->game->paths[i];
  9594 		if (!p->nodes.empty())
  9595 		{
  9596 			PathNode *n = &p->nodes[0];
  9597 			if (p && n)
  9598 			{
  9599 				Vector backPos;
  9600 				if (!p->vox.empty())
  9601 				{
  9602 					if (p->isCoordinateInside(position))
  9603 					{
  9604 						if (p->replayVox == 1)
  9605 						{
  9606 							dsq->voice(p->vox);
  9607 							p->replayVox = 2;
  9608 						}
  9609 						else
  9610 						{
  9611 							dsq->voiceOnce(p->vox);
  9612 						}
  9613 					}
  9614 				}
  9615 				if (!p->warpMap.empty() && p->pathType == PATH_WARP)
  9616 				{
  9617 					int range = 512;
  9618 					int width = 128;
  9619 					switch(p->warpType)
  9620 					{
  9621 					case 'C':
  9622 						if ((position - n->position).getSquaredLength2D() < sqr(range))
  9623 						{
  9624 							warp = true;
  9625 						}
  9626 					break;
  9627 					default:
  9628 					{
  9629 						if (p->isCoordinateInside(position))
  9630 						{
  9631 							warp = true;
  9632 							backPos = p->getBackPos(position);
  9633 						}
  9634 					}
  9635 					break;
  9636 					}
  9637 				}
  9638 				if (warp)
  9639 				{
  9640 					if (avatar->canWarp)
  9641 						dsq->game->warpToSceneFromNode(p);
  9642 					else
  9643 					{
  9644 						avatar->position = backPos;
  9645 						//avatar->vel = -avatar->vel * 0.5;
  9646 						Vector n = p->getEnterNormal();
  9647 						n.setLength2D(avatar->vel.getLength2D());
  9648 						avatar->vel += n;
  9649 						avatar->vel *= 0.5;
  9650 					}
  9651 					return true;
  9652 				}
  9653 				if (core->getNestedMains() == 1 && !riding)
  9654 				{
  9655 					if (p->warpMap.empty() && !p->warpNode.empty())
  9656 					{
  9657 						if (p->isCoordinateInside(position))
  9658 						{
  9659 							Path *p2 = dsq->game->getPathByName(p->warpNode);
  9660 							if (p2)
  9661 							{
  9662 								dsq->game->preLocalWarp(p->localWarpType);
  9663 								dsq->game->avatar->position = p2->getPathNode(0)->position;
  9664 								dsq->game->postLocalWarp();
  9665 								//dsq->fade(0, t);
  9666 								return true;
  9667 							}
  9668 
  9669 						}
  9670 					}
  9671 				}
  9672 			}
  9673 		}
  9674 
  9675 	}
  9676 	for (i = 0; i < dsq->game->warpAreas.size(); i++)
  9677 	{
  9678 		WarpArea *a = &dsq->game->warpAreas[i];
  9679 		if (a->radius)
  9680 		{
  9681 			Vector diff = a->position - this->position;
  9682 			if (diff.getSquaredLength2D() < sqr(a->radius))
  9683 			{
  9684 				if (canWarp)
  9685 				{
  9686 					dsq->game->warpToArea(a);
  9687 					return false;
  9688 				}
  9689 				else
  9690 				{
  9691 					position = lastPosition;
  9692 					vel = -diff;
  9693 					return true;
  9694 				}
  9695 			}
  9696 		}
  9697 		else
  9698 		{
  9699 			if (position.x > a->position.x - a->w && position.x < a->position.x + a->w)
  9700 			{
  9701 				if (position.y > a->position.y - a->h && position.y < a->position.y + a->h)
  9702 				{
  9703 					if (canWarp)
  9704 					{
  9705 						dsq->game->warpToArea(a);
  9706 						return false;
  9707 					}
  9708 					else
  9709 					{
  9710 						position = lastPosition;
  9711 						vel = -vel*1.1;
  9712 					}
  9713 				}
  9714 			}
  9715 		}
  9716 	}
  9717 	return false;
  9718 }
  9719 
  9720 void Avatar::startConversation()
  9721 {
  9722 	closeSingingInterface();
  9723 
  9724 	if (charging)
  9725 	{
  9726 		endCharge();
  9727 	}
  9728 
  9729 	clearTargets();
  9730 }
  9731