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.
2 Copyright (C) 2007, 2010 - Bit-Blot
4 This file is part of Aquaria.
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.
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.
15 See the GNU General Public License for more details.
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.
21 #include "../BBGE/AfterEffect.h"
22 #include "../BBGE/MathFunctions.h"
27 #include "GridRender.h"
29 //#include "CommonEvents.h"
33 //#define AQ_TEST_QUADTRAIL
35 #ifdef AQ_TEST_QUADTRAIL
36 #include "QuadTrail.h"
38 QuadTrail *quadTrail = 0;
41 Path *lastWaterBubble = 0;
42 bool lastJumpOutFromWaterBubble = false;
44 bool useSpiritDistance = true;
45 bool inSpiritWorld = false;
47 const int LAYER_FLOURISH = 3;
49 const float MULT_DMG_CRABCOSTUME = 0.75;
50 const float MULT_DMG_FISHFORM = 1.5;
51 const float MULT_DMG_SEAHORSEARMOR = 0.6;
53 const float MULT_MAXSPEED_BEASTFORM = 1.2;
54 const float MULT_MAXSPEED_FISHFORM = 1.5;
56 const float MULT_DMG_EASY = 0.5;
58 const float JELLYCOSTUME_HEALTHPERC = 0.5;
59 const float JELLYCOSTUME_HEALDELAY = 2.0;
60 const float JELLYCOSTUME_HEALAMOUNT = 0.5;
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;
69 const float fireDelayTime = 0.2;
70 const int maxShieldPoints = 8;
71 const int minMouse = 60;
72 int SongIcon::notesOpen = 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;
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;
94 const float QUICK_SONG_CAST_DELAY = 0.4;
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
101 const float TUMMY_TIME = 6.0;
103 const float chargeMax = 2.0;
105 volatile int micNote = -1;
106 bool openedFromMicInput = false;
108 const int requiredDualFormCharge = 3;
110 bool usingDigital = false;
113 Bone *bone_dualFormGlow = 0;
118 //HRECORD avatarRecord = 0;
120 #define ANIMLAYER_OVERRIDE 4
121 #define ANIMLAYER_UPPERBODYIDLE 6
122 #define ANIMLAYER_HEADOVERRIDE 7
124 Vector Target::getWorldPosition()
129 ret = e->getTargetPoint(targetPt);
134 void Avatar::bindInput()
136 ActionMapper::clearActions();
137 ActionMapper::clearCreatedEvents();
140 dsq->user.control.actionSet.importAction(this, "PrimaryAction", ACTION_PRIMARY);
141 dsq->user.control.actionSet.importAction(this, "SecondaryAction", ACTION_SECONDARY);
143 dsq->user.control.actionSet.importAction(this, "Revert", MakeFunctionEvent(Avatar, revert), 0);
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);
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);
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);
168 dsq->user.control.actionSet.importAction(this, "Look", ACTION_LOOK);
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");
177 dsq->user.control.actionSet.importAction(this, "Roll", ACTION_ROLL);
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);
193 int Avatar::getNotesOpen()
195 return SongIcon::notesOpen;
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()
203 if (dsq->inputMode == INPUT_JOYSTICK)
205 if (!core->joystick.rightStick.isZero())
207 d = core->joystick.rightStick * 300;
212 d = core->joystick.position * 300;
216 else if (dsq->inputMode == INPUT_KEYBOARD)
218 d = dsq->getGameCursorPosition() - position;
223 d = dsq->getGameCursorPosition() - position;
231 void Avatar::postInit()
233 // post init isn't early enough
239 void Avatar::onAnimationKeyPassed(int key)
241 if (swimming && !isRolling() && !bursting && _isUnderWater)
243 if (key == 0 || key == 2)
245 //core->sound->playSfx("SwimKick", 255, 0, 1000+getMaxSpeed()/10.0);
248 Entity::onAnimationKeyPassed(key);
251 void Avatar::doBounce()
256 float len = vel.getLength2D();
258 Vector N = dsq->game->getWallNormal(position);
263 vel = 2*(-I.dot(N))*N + I;
264 vel.setLength2D(len*ba);
268 Vector randCirclePos(Vector position, int radius)
270 float a = ((rand()%360)*(2*PI))/360.0;
271 return position + Vector(sinf(a), cosf(a))*radius;
274 SongIconParticle::SongIconParticle(Vector color, Vector pos, int note)
278 //fastTransform = true;
279 setTexture("particles/glow");
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);
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);
304 setBlendType(RenderObject::BLEND_ADD);
306 float smallestDist = -1;
307 SongIcon *closest = 0;
308 for (int i = 0; i < avatar->songIcons.size(); i++)
312 Vector diff = (position - avatar->songIcons[i]->position);
313 float dist = diff.getSquaredLength2D();
314 if (smallestDist == -1 || dist < smallestDist)
317 closest = avatar->songIcons[i];
321 // find nearest song icon
328 void SongIconParticle::onUpdate(float dt)
334 Vector add = (toIcon->position - position);
335 add.setLength2D(200*dt);
337 velocity.capLength2D(50);
341 SongIcon::SongIcon(int note) : Quad(), note(note)
346 std::ostringstream os;
347 os << "SongIcon" << note;
348 setTexture(os.str());
350 //setTexture("Cursor-Sing");
351 std::ostringstream os;
352 os << "Song/NoteSymbol" << note;
354 setTexture(os.str());
356 scale = Vector(NOTE_SCALE, NOTE_SCALE);
365 noteColor = dsq->getNoteColor(note);
366 //color = dsq->getNoteColor(note)*0.75 + Vector(1,1,1)*0.25;
367 color = dsq->getNoteColor(note);
370 channel = BBGE_AUDIO_NOCHANNEL;
375 glow->setTexture("particles/bigglow");
376 glow->followCamera = 1;
377 glow->rotation.interpolateTo(Vector(0,0,360), 10, -1);
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);
385 void SongIcon::destroy()
390 void SongIcon::spawnParticles(float dt)
395 while (ptimer > intv)
398 SongIconParticle *s = new SongIconParticle(noteColor, randCirclePos(position, 16), note);
399 s->followCamera = true;
400 dsq->game->addRenderObject(s, LR_HUD);
404 void SongIcon::onUpdate(float dt)
408 if (!avatar->singing)
411 if (alpha.x == 0 && !alpha.isInterpolating())
412 alpha.interpolateTo(0.3, 0.1);
419 //channel = BBGE_AUDIO_NOCHANNEL;
438 avatar->setHeadTexture("Singing", 0.1);
442 if ((!openedFromMicInput && isCoordinateInRadius(core->mouse.position, 25)) || micNote == note) // 40 width.x
448 // highlighted for the first time
476 if (alpha.x <= 0 && delay == 0) // && channel != BBGE_AUDIO_NOCHANNEL
483 if (dsq->user.video.noteEffects)
486 if (rippleTimer <= 0)
488 //rippleTimer = 1.0 - ((7 - note)/7.0)*0.7;
489 rippleTimer = 0.5 - (note/7.0)*0.4;
491 if (core->afterEffectManager)
492 core->afterEffectManager->addEffect(new ShockEffect(position,core->screenCenter,0.02,0.015,22,0.2f, 1.2));
499 glow->position = position;
503 void SongIcon::openNote()
505 //if (delay > 0) return;
506 scale.interpolateTo(Vector(1.2, 1.2), 0.1);
508 if (dsq->user.video.noteEffects)
510 glow->scale = Vector(0.5,0.5);
511 glow->scale.interpolateTo(Vector(1.0, 1.0), 2, -1, 1, 1);
513 glow->alpha.interpolateTo(0.6, 0.2, 0, 0, 1);
517 std::ostringstream os;
521 std::string sfx = dsq->game->getNoteName(note);
525 internalOffset = Vector(-5, 0);
526 internalOffset.interpolateTo(Vector(5, 0), 0.08, -1, 1);
528 avatar->singNote(this->note);
530 // this should never get called:
531 if (channel != BBGE_AUDIO_NOCHANNEL)
533 dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
534 //dsq->sound->fadeSfx(channel, SFT_OUT, 0.2);
535 channel = BBGE_AUDIO_NOCHANNEL;
537 //dsq->sound->stopSfx(channel);
542 play.channel = 1 + note;
543 channel = dsq->sound->playSfx(play);
551 float glowLife = 0.5;
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);
562 dsq->game->addRenderObject(q, LR_HUD);
565 std::ostringstream os2;
566 os2 << "Song/NoteSymbol" << note;
568 Quad *q = new Quad(os2.str(), position);
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);
579 dsq->game->addRenderObject(q, LR_HUD);
582 avatar->songInterfaceTimer = 1.0;
586 std::ostringstream os2;
587 os2 << "notesOpen: " << notesOpen;
597 if ((e->position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
602 for (int i = 0; i < dsq->game->paths.size(); i++)
604 Path *p = dsq->game->paths[i];
605 if (!p->nodes.empty())
607 if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
616 void SongIcon::closeNote()
618 //if (delay > 0) return;
619 scale.interpolateTo(Vector(NOTE_SCALE, NOTE_SCALE), 0.1);
621 if (dsq->game->avatar->isSinging() && dsq->user.video.noteEffects)
622 glow->alpha.interpolateTo(0.3, 1.5, 0, 0, 1);
624 glow->alpha.interpolateTo(0, 1.5, 0, 0, 1);
625 glow->scale.interpolateTo(Vector(0.5, 0.5), 0.5);
630 if (channel != BBGE_AUDIO_NOCHANNEL)
632 dsq->sound->fadeSfx(channel, SFT_OUT, 1.0);
633 channel = BBGE_AUDIO_NOCHANNEL;
639 internalOffset.stop();
640 internalOffset = Vector(0,0);
647 int dist = (e->position - dsq->game->avatar->position).getSquaredLength2D();
648 if (e != dsq->game->avatar && dist < sqr(1000))
650 e->songNoteDone(note, len);
653 for (int i = 0; i < dsq->game->paths.size(); i++)
655 Path *p = dsq->game->paths[i];
656 if (!p->nodes.empty())
658 if ((p->nodes[0].position - dsq->game->avatar->position).getSquaredLength2D() < sqr(1000))
660 p->songNoteDone(note, len);
667 std::ostringstream os;
668 os << "notesOpen: " << notesOpen;
675 if (dsq->continuity.form == FORM_NORMAL)
676 avatar->setHeadTexture("");
680 void SongIcon::openInterface()
683 alpha.interpolateTo(1, 0.1);
686 void SongIcon::closeInterface()
690 alpha.interpolateTo(0, 0.1);
693 AvatarState::AvatarState()
699 wasUnderWater = true;
701 lockedToWall = false;
702 crawlingOnWall = false;
708 updateLookAtTime = 0;
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 };
719 bool avatarDebugEnabled = false;
721 void Avatar::toggleMovement(bool on)
726 bool Avatar::isLockable()
728 return (bursting || !_isUnderWater) && (boneLockDelay == 0) && (dsq->continuity.form != FORM_FISH);
731 bool Avatar::isSinging()
738 dsq->continuity.shiftWorlds();
741 void Avatar::applyWorldEffects(WorldType type)
743 static bool oldfh=false;
745 if (type == WT_SPIRIT)
747 //skeletalSprite.transitionAnimate("ball", 0.1, -1);
748 //skeletalSprite.alpha.interpolateTo(0, 1);
749 //skeletalSprite.alpha = 0;
750 //dsq->game->addRenderObject(&skeletalSprite, LR_ENTITIES);
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;
762 oldfh = skeletalSprite.isfh();
763 skeletalSprite.fhTo(isfh());
769 setBlendType(BLEND_ADD);
770 fader->alpha.interpolateTo(0.75, 1);
772 dsq->sound->toggleEffectMusic(SFX_FLANGE, true);
776 //skeletalSprite.transitionAnimate("idle", 1, -1);
777 //skeletalSprite.alpha.interpolateTo(1, 1);
778 //skeletalSprite.alpha = 1;
779 //dsq->game->removeRenderObject(&skeletalSprite);
781 skeletalSprite.setFreeze(false);
782 if (!skeletalSprite.getParent())
784 addChild(&skeletalSprite, PM_STATIC);
786 skeletalSprite.position = Vector(0,0,0);
787 skeletalSprite.scale = Vector(1,1,1);
789 setBlendType(BLEND_DEFAULT);
790 fader->alpha.interpolateTo(0, 1);
792 bool newfh = skeletalSprite.isfh();
793 skeletalSprite.fhTo(oldfh);
794 skeletalSprite.rotation.z = 0;
795 skeletalSprite.rotationOffset.z = 0;
799 dsq->sound->toggleEffectMusic(SFX_FLANGE, false);
803 void Avatar::startFlourish()
805 std::string anim = dsq->continuity.getInternalFormName() + "-flourish";
806 //if (skeletalSprite.getAnimation(anim))
807 Animation *fanim = skeletalSprite.getAnimation(anim);
810 flourishTimer.start(fanim->getAnimationLength()-0.2);
811 flourishPowerTimer.start(fanim->getAnimationLength()*0.5);
813 skeletalSprite.transitionAnimate(anim, 0.1, 0, LAYER_FLOURISH);
816 float rotz = rotationOffset.z;
818 rotationOffset = Vector(0,0,rotz+360);
820 rotationOffset = Vector(0,0,rotz-360);
822 FormType f = dsq->continuity.form;
823 if (f != FORM_NORMAL && f != FORM_BEAST && f != FORM_FISH && f != FORM_SUN && f != FORM_NATURE)
825 rotationOffset.z *= -1;
827 if (f == FORM_ENERGY || f == FORM_DUAL)
829 rotationOffset.z *= 2;
834 Vector v = getNormal();
842 rotationOffset.interpolateTo(Vector(0,0,rotz), 0.8, 0, 0, 1);
845 void Avatar::onIdle()
849 if (dsq->game->li->getState() == STATE_HUG && riding)
851 dsq->game->li->setState(STATE_IDLE);
858 dsq->setMousePosition(Vector(400,300));
860 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
862 closeSingingInterface();
865 dsq->gameSpeed.stopPath();
866 dsq->gameSpeed.interpolateTo(1,0);
869 std::string Avatar::getBurstAnimName()
872 switch(dsq->continuity.form)
884 std::string Avatar::getRollAnimName()
887 switch(dsq->continuity.form)
899 std::string Avatar::getIdleAnimName()
901 std::string ret="idle";
902 switch(dsq->continuity.form)
911 void Avatar::clampPosition()
913 lastPosition = position;
916 void Avatar::updatePosition()
921 void Avatar::debugMsg(const std::string &msg)
923 if (avatarDebugEnabled)
925 BitmapText *txt = new BitmapText(&dsq->font);
927 txt->setDecayRate(1);
928 txt->position = this->position;
930 txt->fadeAlphaWithLife = true;
931 core->getTopStateData()->addRenderObject(txt, LR_DEBUG_TEXT);
935 void Avatar::onBlindTest()
940 void Avatar::onToggleDebugMessages()
942 avatarDebugEnabled = !avatarDebugEnabled;
945 void Avatar::updateHair(float dt)
947 static float hairTimer = 0;
948 Bone *b = skeletalSprite.getBoneByIdx(0);
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));
957 hair->setHeadPosition(headPos);
958 Vector diff = headPos - position;
962 diff2 = diff.getPerpendicularLeft();
964 diff2 = diff.getPerpendicularRight();
966 Vector diff3 = position - headPos;
968 if (state.lockedToWall && wallPushVec.y < 0 && (fabs(wallPushVec.y) > fabs(wallPushVec.x)))
972 diff3 = Vector(-50, -25);
975 diff3 = Vector(50,-25);
978 float len =diff2.getLength2D();
979 diff3.setLength2D(len);
982 diff = (diff + diff2)/2.0;
986 while (hairTimer > 2.0)
990 float useTimer = hairTimer;
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)));
1000 diff.setLength2D(400);
1001 //if (!vel.isLength2DIn(10))
1002 hair->exertForce(diff, dt);
1006 diff.setLength2D(400);
1007 hair->exertForce(diff, dt);
1010 hair->exertForce(vel2, dt);
1011 hair->updatePositions();
1015 void Avatar::updateDamageVisualEffects()
1017 int damageThreshold = float(maxHealth/5.0)*3.0f;
1018 if (health <= damageThreshold)
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);
1025 std::ostringstream os;
1026 os << "damageSprite alpha: " << a;
1030 dsq->game->damageSprite->scale = Vector(1,1);
1031 dsq->game->damageSprite->scale.interpolateTo(Vector(1.2, 1.2), 0.5, -1, 1);
1036 dsq->game->sceneColor.interpolateTo(Vector(1,0.5,0.5), 0.75);
1042 dsq->game->damageSprite->alpha.interpolateTo(0, 0.3);
1046 void Avatar::checkUpgradeForShot(Shot *s)
1048 if (dsq->continuity.energyMult <= 1)
1049 s->extraDamage = dsq->continuity.energyMult * 1;
1051 s->extraDamage = dsq->continuity.energyMult * 0.75;
1053 if (s->extraDamage > 0)
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);
1065 void Avatar::onDamage(DamageData &d)
1067 Entity::onDamage(d);
1070 if (dsq->difficulty == DSQ::DIFF_EASY)
1073 d.damage *= MULT_DMG_EASY;
1076 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
1077 if (dsq->continuity.form == FORM_NORMAL)
1079 if (nocasecmp(dsq->continuity.costume, "CC")==0)
1081 d.damage *= MULT_DMG_CRABCOSTUME;
1087 if (nocasecmp(dsq->continuity.costume, "seahorse")==0)
1089 d.damage *= MULT_DMG_SEAHORSEARMOR;
1093 if (dsq->continuity.form == FORM_FISH)
1095 d.damage *= MULT_DMG_FISHFORM;
1098 if ((core->isNested() && dsq->game->invincibleOnNested) || dsq->game->invinciblity)
1101 d.damageType = DT_NONE;
1105 if (d.damageType == DT_ENEMY_INK)
1107 setBlind(d.effectTime);
1111 if (d.damageType == DT_ENEMY_POISON)
1113 dsq->continuity.setPoison(1, d.effectTime);
1116 if (dsq->continuity.defenseMultTimer.isActive())
1118 d.damage *= dsq->continuity.defenseMult;
1121 if (dsq->continuity.invincibleTimer.isActive())
1126 if ((health - d.damage) <= 0)
1131 if ((health - i) > 0)
1142 if ((!invincible || !dsq->game->invincibleOnNested) && !(invincibleBreak && damageTimer.isActive() && d.useTimer) && !dsq->continuity.invincibleTimer.isActive())
1144 if (d.damageType == DT_ENEMY_ACTIVEPOISON)
1145 core->sound->playSfx("Poison");
1147 core->sound->playSfx("Pain");
1150 setHeadTexture("Pain", 1);
1152 int r = (rand()%2)+1;
1153 std::ostringstream os;
1154 os << "basicHit" << r;
1155 skeletalSprite.transitionAnimate(os.str(), 0.05, 0, ANIMLAYER_OVERRIDE);
1160 // this will probably cause a crash!
1161 state.lookAtEntity = d.attacker;
1167 float healthWillBe = health-d.damage;
1168 // determines length of shader blur as well
1170 if (healthWillBe<=0)
1173 dsq->rumble(d.damage, d.damage, 0.4);
1176 //dsq->shakeCamera(5, t);
1179 float shake = d.damage*2;
1182 dsq->shakeCamera(shake, t);
1185 if (healthWillBe <= 2 && d.damageType != DT_ENEMY_ACTIVEPOISON)
1187 //if (!dsq->gameSpeed.isInterpolating() && dsq->gameSpeed.x==1)
1190 dsq->gameSpeed.stop();
1191 dsq->gameSpeed.stopPath();
1192 dsq->gameSpeed.x = 1;
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);
1200 dsq->sound->playSfx("heartbeat");
1202 if (healthWillBe < 2 && healthWillBe >= 1 && !dsq->game->hasPlayedLow)
1204 dsq->emote.playSfx(EMOTE_NAIJALOW);
1205 dsq->game->hasPlayedLow = 1;
1209 dsq->gameSpeed.path.clear();
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);
1217 dsq->gameSpeed.startPath(2);
1219 //dsq->gameSpeed.interpolateTo(0.7, 3);
1224 hitEmitter.load("NaijaHit");
1232 void Avatar::playHitSound()
1234 int hitSound = (rand()%8)+1;
1235 static int lastHitSound = 0;
1236 if (lastHitSound == hitSound)
1242 std::ostringstream os;
1243 os << "hit" << hitSound;
1244 core->sound->playSfx(os.str());
1247 const int beatHealth = 3;
1248 void Avatar::updateHeartbeatSfx(float t)
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);
1261 BASS_ChannelSlideAttributes(heartbeat, useFreq, vol, -101, 1000.0*t);
1266 void Avatar::onHealthChange(float change)
1268 updateDamageVisualEffects();
1270 if (health <= beatHealth && health > 0)
1275 //debugLog("starting heartbeat");
1276 heartbeat = core->sound->playSfx("Heartbeat", 255, 0, 1000, 1);
1277 //core->sound->playSfx("Heartbeat");
1280 updateHeartbeatSfx(0.5);
1282 if (health > beatHealth)
1287 //debugLog("stopping heartbeat");
1288 BASS_CHANNELINFO info;
1289 BASS_ChannelGetInfo(heartbeat, &info);
1290 BASS_ChannelSlideAttributes(heartbeat, info.freq, -2, -101, 1000*2);
1297 void Avatar::revive()
1304 void Avatar::updateDualFormChargeEffects()
1308 void Avatar::lostTarget(int i, Entity *e)
1310 dsq->sound->playSfx("target-unlock");
1313 void Avatar::entityDied(Entity *e)
1315 Entity::entityDied(e);
1316 for (int i = 0; i < targets.size(); i++)
1318 if (targets[i].e == e)
1322 targetUpdateDelay = 100;
1328 if (state.lookAtEntity==e)
1329 state.lookAtEntity = 0;
1332 if (e->isGoingToBeEaten())
1334 EatType et = e->getEatType();
1339 dsq->continuity.eatBeast(e->eatData);
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)
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");
1359 std::ostringstream os;
1360 os << "lastDamage.form = " << e->lastDamage.form;
1365 void Avatar::enableInput()
1367 ActionMapper::enableInput();
1368 dsq->game->toggleMiniMapRender(1);
1370 if (!dsq->game->isApplyingState())
1371 dsq->toggleCursor(true);
1375 dsq->setMousePosition(Vector(400,300));
1378 if (dsq->continuity.form == FORM_ENERGY)
1380 for (int i = 0; i < targetQuads.size(); i++)
1381 targetQuads[i]->start();
1384 setInvincible(false);
1385 // can't do that here, cause it'll break the hug
1386 //stillTimer.stop();
1389 void Avatar::disableInput()
1391 ActionMapper::disableInput();
1393 // can't do that here, cause it'll break the hug
1394 //stillTimer.stop();
1396 closeSingingInterface();
1397 dsq->game->toggleMiniMapRender(0);
1398 dsq->toggleCursor(false);
1403 dsq->setMousePosition(Vector(400,300));
1406 for (int i = 0; i < targetQuads.size(); i++)
1408 targetQuads[i]->stop();
1411 setInvincible(true);
1414 void Avatar::clearTargets()
1416 for (int i = 0; i < targets.size(); i++)
1426 void Avatar::slowToRest()
1428 vel.capLength2D(50);
1430 if (vel.getSquaredLength2D() > sqr(50))
1432 vel.setLength2D(50);
1435 bursting = swimming = false;
1436 skeletalSprite.stopAnimation(1);
1437 rotation.interpolateTo(Vector(0,0,0), 0.2, 0, 0, 1);
1441 #define SPECWIDTH 368
1442 #define SPECHEIGHT 127
1446 volatile int curMicNote = -1, lastMicNote=-1;
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;
1457 BOOL CALLBACK recordCallback(HRECORD handle, const void *buf, DWORD len, DWORD user)
1460 #ifdef BBGE_BUILD_RECORD
1461 if (inMe) return TRUE;
1464 //if (!dsq->game->avatar->isSinging()) return FALSE;
1469 QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
1471 QueryPerformanceCounter((LARGE_INTEGER*)&curTime);
1477 float fft[4096]; // get the FFT data
1478 BASS_ChannelGetData(handle,fft,BASS_DATA_FFT4096);
1485 //for (int i = 5; i < 13; i++)
1486 for (int i = 12; i < 64; i++)
1488 if (fft[i] > 0.01 && (fft[i] > largest || largest == -1))
1497 // find the next largest
1499 float dt = (double(curTime-lastTick)/double(freq));
1506 int octave = dsq->user.audio.octave;
1508 int minNote = 12;///6;
1509 int maxNote = minNote + 25; // 8
1512 minNote += (octRange)*octave;
1513 maxNote += (octRange)*octave;
1515 posMicNote = dsq->fftnotes.getNoteFromFFT(v, octave);
1517 //if (lastLargest<largest || fabs(largest-lastLargest) < 0.05)
1520 lastLargest = largest;
1522 float closeRange = 0.05;
1523 // check for our e natural
1525 if (posMicNote != -1)
1528 curMicNote = posMicNote;
1541 timeFromLastNote += dt;
1542 if (curMicNote != lastMicNote)
1543 timeFromLastNote = 0;
1544 if (timeFromLastNote > 0.0001)
1546 micNote = curMicNote;
1547 timeFromLastNote = 0;
1549 if (curMicNote == -1)
1551 lastMicNote = curMicNote;
1561 class FoodIcon2 : public Quad
1565 class FoodIcon : public Quad
1568 FoodIcon(IngredientEffectType iet);
1570 IngredientEffectType type;
1575 void Avatar::openFoodInterface()
1577 if (!singing && !pickingPullTarget && health > 0 && !isEntityDead() && !blockSinging)
1582 foodIcons.resize(8);
1587 void Avatar::openSingingInterface()
1589 if (!singing && !pickingPullTarget && health > 0 && !isEntityDead() && !blockSinging)
1591 //core->mouse.position = Vector(400,300);
1592 if (dsq->inputMode != INPUT_MOUSE)
1594 core->centerMouse();
1595 //core->setMousePosition(Vector(400,300));
1598 core->setMouseConstraintCircle(singingInterfaceRadius);
1601 currentSongIdx = SONG_NONE;
1603 // make the singing icons appear
1604 for (int i = 0; i < songIcons.size(); i++)
1606 songIcons[i]->openInterface();
1608 currentSong.notes.clear();
1611 songInterfaceTimer = 0;
1613 dsq->game->songLineRender->clear();
1617 // if (dsq->useMic && !dsq->autoSingMenuOpen && dsq->user.audio.micOn)
1619 // //avatarRecord=BASS_RecordStart(44100,1,0,&recordCallback,0);
1620 // BASS_ChannelPlay(avatarRecord, false);
1624 if (dsq->inputMode == INPUT_JOYSTICK)
1626 core->setMousePosition(core->center);
1631 void Avatar::closeSingingInterface()
1634 if (dsq->game->songLineRender)
1635 dsq->game->songLineRender->clear();
1638 core->setMouseConstraint(false);
1639 usingDigital = false;
1640 quickSongCastDelay = 1;
1642 // HACK: this prevents being "locked" away from the seahorse... so naija can
1643 // be in singing range of the seahorse
1644 applyRidingPosition();
1647 for (int i = 0; i < songIcons.size(); i++)
1649 songIcons[i]->closeInterface();
1652 if (dsq->continuity.form == FORM_NORMAL)
1655 currentSongIdx = dsq->continuity.checkSongAssisted(currentSong);
1656 if (currentSongIdx != SONG_NONE)
1658 dsq->continuity.castSong(currentSongIdx);
1659 currentSongIdx = SONG_NONE;
1665 if (dsq->useMic && !dsq->autoSingMenuOpen && dsq->user.audio.micOn)
1667 //BASS_ChannelStop(avatarRecord);
1668 BASS_ChannelPause(avatarRecord);
1669 //BASS_RecordFree();
1676 lastMicNote = curMicNote = micNote = -1;
1680 void Avatar::openFormInterface()
1682 if (!inFormInterface)
1684 inFormInterface = true;
1686 for(int i = 0; i < formIcons.size(); i++)
1688 formIcons[i]->alpha.interpolateTo(1, 0.1);
1693 void Avatar::closeFormInterface()
1695 if (inFormInterface)
1697 inFormInterface = false;
1699 for (int i = 0; i < formIcons.size(); i++)
1701 formIcons[i]->alpha.interpolateTo(0, 0.1);
1706 void Avatar::toggleCape(bool on)
1716 void Avatar::refreshDualFormModel()
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");
1725 void Avatar::updateDualFormGlow(float dt)
1727 if (dsq->continuity.form == FORM_DUAL && bone_dualFormGlow)
1731 if (requiredDualFormCharge != 0)
1732 perc = float(dsq->continuity.dualFormCharge)/float(requiredDualFormCharge);
1735 bone_dualFormGlow->alpha = perc*0.5 + 0.1;
1736 bone_dualFormGlow->scale.interpolateTo(Vector(perc, perc), 0.2);
1740 void Avatar::changeForm(FormType form, bool effects, bool onInit, FormType lastForm)
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));
1751 pullTarget->stopPull();
1756 if (form == FORM_DUAL && !dsq->continuity.hasLi())
1759 if (!canChangeForm) return;
1761 std::ostringstream os;
1762 os << "changeForm: " << form;
1767 dsq->game->clearControlHint();
1770 if (lastForm == FORM_NONE)
1771 lastForm = dsq->continuity.form;
1775 std::ostringstream os2;
1776 os2 << "lastForm: " << lastForm;
1777 debugLog(os2.str());
1779 for (int i = 0; i < targetQuads.size(); i++)
1782 targetQuads[i]->stop();
1786 if (bone_dualFormGlow)
1787 bone_dualFormGlow->scale = 0;
1791 if (form != FORM_NORMAL)
1798 // check nearby area
1799 //bool isTooCloseToWall=false;
1801 if (isNearObstruction(3))
1803 Vector n = dsq->game->getWallNormal(position);
1812 //rotationOffset.interpolateTo(Vector(0,0,0), 0.5);
1816 lightFormGlow->alpha.interpolateTo(0, 0.5);
1817 lightFormGlowCone->alpha.interpolateTo(0, 0.5);
1820 //position.interpolateTo(bodyPosition, 2, 0);
1821 position = bodyPosition;
1822 dsq->continuity.warpLiToAvatar();
1823 spiritBeaconEmitter.start();
1826 //dsq->game->sceneColor3.interpolateTo(Vector(1, 1, 1), 0.5);
1829 if (dsq->continuity.hasLi())
1831 dsq->game->li->alpha = 1;
1832 dsq->game->li->position = position;
1833 dsq->game->li->setState(STATE_IDLE);
1837 if (leftHandEmitter && rightHandEmitter)
1839 leftHandEmitter->stop();
1840 rightHandEmitter->stop();
1846 state.abilityDelay = 0;
1847 formAbilityDelay = 0;
1848 dsq->continuity.form = form;
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));
1859 core->sound->playSfx("EnergyForm");
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);
1869 dsq->game->tintColor = Vector(1,1,3);
1870 dsq->game->tintColor.interpolateTo(Vector(1,1,1), 1);
1875 core->sound->playSfx("NormalForm");
1878 core->sound->playSfx("BeastForm");
1881 core->sound->playSfx("FishForm");
1884 core->sound->playSfx("SunForm");
1887 core->sound->playSfx("NatureForm");
1890 spiritBeaconEmitter.start();
1893 core->sound->playSfx("DualForm");
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);
1903 setv(EV_NOINPUTNOVEL, 1);
1904 dsq->overlay->alpha.interpolateTo(0, 0.2);
1905 dsq->overlay->color.interpolateTo(0, 0.4);
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);
1913 if (form != FORM_ENERGY)
1915 dsq->game->sceneColor3.interpolateTo(Vector(1,1,1), 0.2);
1918 float lastHairAlphaMod = 0;
1922 lastHairAlphaMod = hair->alphaMod;
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();
1939 setBoneLock(BoneLock());
1941 refreshModel("FishForm", "");
1942 //rotationOffset.interpolateTo(Vector(0,0,-90), 0.5);
1943 //refreshModel("NaijaFish", "");
1948 refreshModel("Naija", "SunForm");
1949 lightFormGlow->moveToFront();
1950 lightFormGlow->alpha.interpolateTo(0.75, 1);
1951 lightFormGlowCone->alpha.interpolateTo(0.4, 1);
1953 lightFormGlow->alphaMod = 0;
1954 lightFormGlowCone->alphaMod = 0;
1959 if (lastForm == FORM_SPIRIT)
1961 dsq->continuity.shiftWorlds();
1964 refreshNormalForm();
1965 //skeletalSprite.loadSkeletal("child");
1969 refreshModel("Naija", "NatureForm");
1972 hair->setTexture("Naija/Cape-NatureForm");
1973 hair->alphaMod = 1.0;
1976 skeletalSprite.loadSkin("ChildTeira");
1980 if (dsq->game->sceneNatureForm == "forest")
1982 debugLog("Forest Form");
1983 dsq->continuity.form = FORM_NATURE_FOREST;
1985 else if (dsq->game->sceneNatureForm == "sun")
1987 dsq->continuity.form = FORM_NATURE_SUN;
1988 debugLog("Sun Form");
1990 else if (dsq->game->sceneNatureForm == "fire")
1992 dsq->continuity.form = FORM_NATURE_FIRE;
1993 debugLog("Fire Form");
1995 else if (dsq->game->sceneNatureForm == "dark")
1997 dsq->continuity.form = FORM_NATURE_DARK;
1998 debugLog("Dark Form");
2000 else if (dsq->game->sceneNatureForm == "rock" || dsq->game->sceneNatureForm.empty())
2002 dsq->continuity.form = FORM_NATURE_ROCK;
2003 debugLog("Rock Form");
2010 refreshModel("Naija", "BeastForm");
2014 bodyPosition = position;
2015 bodyOffset = offset;
2017 dsq->continuity.shiftWorlds();
2021 skeletalSprite.alphaMod = 0;
2022 canChangeForm = false;
2023 useSpiritDistance = false;
2024 inSpiritWorld = true;
2028 hair->alphaMod = lastHairAlphaMod;
2033 if (dsq->continuity.hasLi())
2035 dsq->game->li->setState(STATE_WAIT);
2036 dsq->game->li->alpha = 0;
2038 //dualFormMode = DUALFORM_LI;
2039 refreshDualFormModel();
2041 for (int i = 0; i < targetQuads.size(); i++)
2042 targetQuads[i]->start();
2051 avatar->enableInput();
2054 //idle();//skeletalSprite.animate("idle", -1, 0);
2058 int Avatar::getLastNote()
2063 void Avatar::singNote(int note)
2065 currentSong.notes.push_back(note);
2067 //int song = dsq->continuity.checkSong(currentSong);
2068 //int song = dsq->continuity.checkSongAssisted(currentSong);
2069 //int song = dsq->continuity.checkSongAssisted(currentSong);
2071 std::ostringstream os;
2072 os << "sung note: " << note;
2075 //currentSongIdx = song;
2081 std::ostringstream os;
2082 os << "Sung Song: " << song;
2085 // close in a few seconds
2086 //closeSingingInterface();
2090 void Avatar::updateSingingInterface(float dt)
2094 if (songIcons.size()>0 && songIcons[0]->alpha.x > 0)
2096 if (dsq->inputMode != INPUT_JOYSTICK && !core->mouse.change.isZero())
2098 if (dsq->game->songLineRender && songIcons[0]->alpha.x == 1)
2100 int smallestDist = -1;
2102 for (int i = 0; i < songIcons.size(); i++)
2104 int dist = (songIcons[i]->position - core->mouse.position).getSquaredLength2D();
2105 if (smallestDist == -1 || dist < smallestDist)
2107 smallestDist = dist;
2112 dsq->game->songLineRender->newPoint(core->mouse.position, songIcons[closest]->noteColor);
2116 if (health <= 0 || isEntityDead())
2118 closeSingingInterface();
2122 //if (dsq->inputMode == INPUT_JOYSTICK)
2124 //core->mouse.position += core->joystick.position * dsq->user.control.joyCursorSpeed;
2127 cursorPos.update(dt);
2128 if (cursorPos.isInterpolating())
2133 int cursorRadius = singingInterfaceRadius-8;
2137 static float returnDelay = 0;
2142 // was recently in...
2143 if (core->joystick.dpadLeft || isActing(ACTION_SWIMLEFT))
2147 usingDigital = true;
2149 if (core->joystick.dpadRight || isActing(ACTION_SWIMRIGHT))
2151 //debugLog("right");
2153 usingDigital = true;
2156 if (core->joystick.dpadDown || isActing(ACTION_SWIMDOWN))
2160 usingDigital = true;
2162 if (core->joystick.dpadUp || isActing(ACTION_SWIMUP))
2166 usingDigital = true;
2171 static float dpadTimer = 0;
2172 static Vector lastDesired;
2174 if (desired == lastDesired)
2177 if (dpadTimer > 0.05)
2190 desired.setLength2D(cursorRadius);
2191 core->mouse.position = Vector(400,300)+desired;
2192 core->setMousePosition(core->mouse.position);
2195 lastDesired = desired;
2198 //desired.setLength2D(cursorRadius);
2202 int spd = 1500;//850;
2204 desired.setLength2D(spd*dt);
2207 if (dsq->inputMode == INPUT_JOYSTICK)// && !dsq->joystick.position.isLength2DIn(0.4))
2210 //if (!dsq->joystick.position.isLength2DIn(0.9))
2212 Vector p(core->center);
2213 Vector d = dsq->joystick.position;
2216 d.x = float(int((d.x*2)))/4.0;
2217 d.y = float(int((d.y*2)))/4.0;
2221 if (!d.isLength2DIn(0.6))
2226 p = p + d*cursorRadius-1;
2227 core->setMousePosition(p);
2230 else if (desired.x != 0 || desired.y != 0)
2233 //debugLog("desired not zero");
2234 //debugLog("mouse position set");
2235 //core->mouse.position = Vector(400,300)+desired;
2236 core->mouse.position += desired;
2238 Vector diff = core->mouse.position - Vector(400,300);
2239 if (!diff.isLength2DIn(cursorRadius))
2241 diff.setLength2D(cursorRadius);
2242 core->mouse.position = Vector(400,300) +diff;
2244 core->setMousePosition(core->mouse.position);
2246 cursorPos.interpolateTo(desired, 0.1);
2247 core->setMousePosition(cursorPos);
2250 else if (usingDigital)
2252 if (returnDelay <= 0)
2254 debugLog("desired is zero");
2255 Vector c = core->center;
2256 Vector dir = c - core->mouse.position;
2257 if (dir.isLength2DIn(8))
2259 debugLog("in range");
2260 core->mouse.position = c;
2264 debugLog("NOT in range");
2265 dir.setLength2D(dt * 400);
2266 core->mouse.position += dir;
2268 core->setMousePosition(core->mouse.position);
2276 //(core->joystick.position * (singingInterfaceRadius-16)) + Vector(400,300);
2279 //Vector dist = dsq->getGameCursorPosition() - dsq->game->avatar->position;
2281 Vector dist = core->mouse.position - Vector(400,300);
2282 int checkRad = singingInterfaceRadius-10;
2283 if (dist.getSquaredLength2D() > sqr(checkRad))
2286 core->setMousePosition(Vector(400,300)+dist);
2291 static float timer=0;
2295 if (dist.getSquaredLength2D() > sqr(singingInterfaceRadius))
2297 dist |= singingInterfaceRadius-10;
2298 //core->mouse.position = dsq->game->avatar->position + dist;
2303 //core->setMousePosition(Vector(400,300)+dist/*dsq->game->avatar->position + dist - core->screenCenter*/);
2304 //HACK: constrain the mouse to the circle
2306 if (dist.getSquaredLength2D() > sqr(singingInterfaceRadius*core->invGlobalScale))//*core->invGlobalScale))
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);
2317 setSongIconPositions();
2323 void Avatar::setSongIconPositions()
2325 float radIncr = (2*PI)/float(songIcons.size());
2327 for (int i = 0; i < songIcons.size(); i++)
2329 songIcons[i]->position = Vector(400,300)+/*this->position + */Vector(sin(rad)*singingInterfaceRadius, cos(rad)*singingInterfaceRadius);
2334 const int chkDist = 2500*2500;
2336 Target Avatar::getNearestTarget(const Vector &checkPos, const Vector &distPos, Entity *source, DamageType dt, bool override, std::vector<Target> *ignore, EntityList *entityList)
2338 BBGE_PROF(Avatar_getNearestTarget);
2340 entityList = &dsq->entities;
2343 Vector targetPosition;
2345 Entity *closest = 0;
2346 int highestPriority = -999;
2347 int smallestDist = -1;
2354 for (j = 0; j < targets.size(); j++)
2356 if (targets[j].e == e) break;
2358 if (j != targets.size()) continue;
2362 if (e != this && e->targetPriority >= highestPriority && this->pullTarget != e && e->isDamageTarget(dt) && dsq->game->isValidTarget(e, this))
2367 if (e->position.isNan())
2370 std::ostringstream os;
2371 os << "NAN position entity name: " << e->name << " type: " << e->getEntityType();
2377 int dist = (e->position - position).getSquaredLength2D();
2380 int numTargetPoints = e->getNumTargetPoints();
2381 bool clearAfter = false;
2382 if (numTargetPoints == 0)
2387 for (; j < ignore->size(); j++)
2389 if ((*ignore)[j].e == e)
2392 if (j != ignore->size()) continue;
2394 e->addTargetPoint(e->getEnergyShotTargetPosition());
2396 numTargetPoints = 1;
2398 if (numTargetPoints > 0)
2400 for (int i = 0; i < numTargetPoints; i++)
2405 for (; j < ignore->size(); j++)
2407 if ((*ignore)[j].e == e && (*ignore)[j].targetPt == i)
2410 if (j != ignore->size()) continue;
2412 int dist = (e->getTargetPoint(i) - distPos).getSquaredLength2D();
2413 //int dist = (e->getTargetPoint(i) - distPos).getLength2D();
2414 if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
2416 if (override || (checkPos - e->getTargetPoint(i)).isLength2DIn(64))
2418 dist = (e->getTargetPoint(i) - checkPos).getSquaredLength2D();
2419 if (smallestDist == -1 || dist < smallestDist)
2421 highestPriority = e->targetPriority;
2422 targetPosition = e->getTargetPoint(i);
2424 smallestDist = dist;
2432 e->clearTargetPoints();
2438 t.pos = targetPosition;
2439 t.targetPt = targetPt;
2443 float maxTargetDelay = 0.5;
2444 bool wasDown = false;
2445 void Avatar::updateTargets(float dt, bool override)
2447 DamageType damageType = DT_AVATAR_ENERGYBLAST;
2448 for (int i = 0; i < targets.size(); i++)
2451 || !targets[i].e->isPresent()
2452 || targets[i].e->getState() == STATE_DEATHSCENE
2453 || !dsq->game->isValidTarget(targets[i].e, this))
2459 if ((dsq->inputMode == INPUT_MOUSE || dsq->inputMode == INPUT_KEYBOARD) && !(wasDown && core->mouse.buttons.right))
2464 mod = maxTargetDelay*10;
2465 targetUpdateDelay += dt*mod;
2468 if (targetUpdateDelay > maxTargetDelay || override)
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*/
2475 // crappy hack for now, assuming one target:
2479 Vector dir = getAim();
2480 Vector checkPos = position + dir;
2481 Vector distPos = position;
2482 if (!(dsq->getGameCursorPosition() - distPos).isLength2DIn(4))
2485 t = getNearestTarget(checkPos, distPos, this, damageType, override, &targets);
2488 //if ((t.getWorldPosition() - dsq->getGameCursorPosition()).isLength2DIn(64))
2493 targets.push_back(t);
2495 targetUpdateDelay = 0;
2496 if (!override && core->mouse.buttons.right)
2498 maxTargetDelay = 90;
2500 dsq->spawnParticleEffect("TargetAquired", t.pos);
2506 if (targets.empty())
2508 for (int i = 0; i < oldTargets.size(); i++)
2510 Entity *e = oldTargets[i].e;
2513 int dist = (e->getTargetPoint(oldTargets[i].targetPt) - distPos).getSquaredLength2D();
2514 if (dist < sqr(TARGET_RANGE+e->getTargetRange()))
2516 targets.push_back(oldTargets[i]);
2530 for (int i = 0; i < targets.size(); i++)
2532 Entity *e = targets[i].e;
2535 if (!(position - e->position).isLength2DIn(e->getTargetRange() + TARGET_RANGE + TARGET_GRACE_RANGE) || !dsq->game->isValidTarget(e, this) || !e->isDamageTarget(damageType))
2537 lostTarget(i, targets[i].e);
2539 targetUpdateDelay = maxTargetDelay;
2547 void Avatar::loseTargets()
2549 for (int i = 0; i < targets.size(); i++)
2551 Entity *e = targets[i].e;
2554 lostTarget(i, targets[i].e);
2556 targetUpdateDelay = maxTargetDelay;
2561 void Avatar::updateTargetQuads(float dt)
2564 particleManager->setSuckPosition(1, dsq->getGameCursorPosition());
2567 for (int i = 0; i < targetQuads.size(); i++)
2573 static Entity *lastTargetE = 0;
2574 const float tt = 0.02;
2575 for (int i = 0; i < targets.size(); i++)
2580 targetQuads[i]->alpha.interpolateTo(1, 0.1);
2581 Entity *e = targets[i].e;
2582 if (lastTargetE != e)
2584 dsq->sound->playSfx("target-lock");
2589 //targetQuads[i]->position.interpolateTo(targets[i].pos, 0.01);
2591 targetQuads[i]->position.interpolateTo(targets[i].pos, tt);
2592 targets[i].pos = e->getTargetPoint(targets[i].targetPt);
2595 particleManager->setSuckPosition(1, targets[i].pos);
2599 Emitter *em = targetQuads[i];
2600 if (!em->isRunning())
2608 targetQuads[i]->position = dsq->getGameCursorPosition();
2609 //targetQuads[i]->alpha.interpolateTo(0, 0.1);
2613 if (targets.empty())
2615 for (int i = 0; i < targetQuads.size(); i++)
2617 if (lastTargetE != 0)
2621 //targetQuads[i]->position.interpolateTo(dsq->getGameCursorPosition(),tt);
2623 std::ostringstream os;
2624 os << "setting targetQuads[i] to game cursor, is running = " << targetQuads[i]->isRunning();
2628 targetQuads[i]->position = dsq->getGameCursorPosition();
2629 if (dsq->continuity.form == FORM_ENERGY && isInputEnabled())
2631 if (dsq->inputMode == INPUT_JOYSTICK && targetQuads[i]->isRunning())
2633 targetQuads[i]->stop();
2635 else if (dsq->inputMode != INPUT_JOYSTICK && !targetQuads[i]->isRunning())
2637 targetQuads[i]->start();
2642 if (targetQuads[i]->isRunning())
2644 targetQuads[i]->stop();
2651 //fireAtNearestValidEntity("Fire", DT_AVATAR_ENERGYBLAST);
2652 bool Avatar::fireAtNearestValidEntity(const std::string &shot)
2654 if (state.swimTimer > 0)
2655 state.swimTimer -= 0.5;
2657 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
2659 targetUpdateDelay = 0;
2660 if (targetUpdateDelay < 0)
2661 targetUpdateDelay = 0;
2666 Vector p = position;
2667 p = boneLeftArm->getWorldPosition();
2668 //&& !dsq->game->isObstructed(TileVector(position))
2670 if (dsq->inputMode == INPUT_MOUSE && state.lockedToWall )
2671 dir = dsq->getGameCursorPosition() - p;
2676 ShotData *shotData = Shot::getShotData(shot);
2679 bool aimAt = (dir.z == 1.0);
2680 //bool aimAt = true;
2682 Entity *closest = 0;
2683 Vector targetPosition;
2687 //std::vector<Target>targets;
2689 bool firedShot = false;
2694 if (dsq->inputMode != INPUT_JOYSTICK && vel.isLength2DIn(50))
2708 if (!dir.isLength2DIn(2))
2712 bool clearTargets = false;
2714 // allow autoAim if desired
2715 if ((dsq->inputMode == INPUT_JOYSTICK && !aimAt) || dsq->user.control.autoAim)
2717 if (targets.empty())
2719 // force a grab of the nearest targets
2720 updateTargets(shotData->damageType, true);
2721 // clear the targets after
2722 clearTargets = true;
2726 if (!targets.empty())
2729 for (int i = 0; i < targets.size(); i++)
2734 dir = targets[i].pos - p;
2738 std::ostringstream os;
2739 os << "shotdir(" << dir.x << ", " << dir.y << ")";
2745 Vector oldDir = dir;
2748 dir = (dir + oldDir)/2.0;
2753 dir = (targets[i].e->getTargetPoint(targets[i].targetPt) - p);
2756 s = dsq->game->fireShot(shot, this, targets[i].e);
2757 s->setAimVector(dir);
2758 s->setTargetPoint(targets[i].targetPt);
2761 if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2763 s = dsq->game->fireShot("EnergyBlast2", this, targets[i].e);
2764 s->setAimVector(dir);
2765 s->setTargetPoint(targets[i].targetPt);
2769 s = dsq->game->fireShot("EnergyBlast", this, targets[i].e);
2770 s->setAimVector(dir);
2771 s->setTargetPoint(targets[i].targetPt);
2778 //if (!dir.isLength2DIn(2) || dsq->inputMode == INPUT_JOYSTICK)
2781 s = dsq->game->fireShot(shot, this);
2783 if (dir.isLength2DIn(2))
2785 if (!vel.isLength2DIn(2))
2786 s->setAimVector(vel);
2787 else // standing still
2789 Vector dir = getForward();
2792 dir = dir.getPerpendicularRight();
2795 dir = dir.getPerpendicularLeft();
2796 s->setAimVector(dir);
2801 s->setAimVector(dir);
2804 if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
2806 s = dsq->game->fireShot("EnergyBlast2", this);
2807 s->setAimVector(dir);
2811 s = dsq->game->fireShot("EnergyBlast", this);
2812 s->setAimVector(dir);
2820 checkUpgradeForShot(s);
2824 skeletalSprite.transitionAnimate("fireBlast", 0.1, 0, 5);
2826 //s->damageType = dt;
2828 if (!targets.empty())
2829 s->damage = float(damage)/float(targets.size());
2837 // try to avoid targets sticking
2838 updateTargetQuads(shotData->damageType);
2844 void Avatar::spawnSeed()
2846 // max spore children/seeds = 50
2847 if (dsq->game->getNumberOfEntitiesNamed("SporeChild") < 4)
2849 if (!dsq->game->isObstructed(TileVector(position)))
2851 Entity *seed = dsq->game->createEntity("SporeChild", 0, position, 0, 0, "");
2856 // visual effect and/or sound effect
2860 Vector Avatar::getFacing()
2862 if (vel.isLength2DIn(2) && rotation.z == 0)
2867 return Vector(-1,0);
2869 return getForward();
2872 void Avatar::switchDualFormMode()
2874 //debugLog("dualForm: changing");
2876 dsq->sound->playSfx("dualform-switch");
2878 dsq->overlay->color = Vector(1,1,1);
2882 if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
2883 dsq->continuity.dualFormMode = Continuity::DUALFORM_LI;
2885 dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
2887 refreshDualFormModel();
2890 bool Avatar::hasThingToActivate()
2892 return ((pathToActivate != 0) || (entityToActivate != 0));
2895 void Avatar::formAbility(int ability)
2897 if (hasThingToActivate()) return;
2898 //debugLog("form ability function");
2899 switch(dsq->continuity.form)
2903 debugLog("dual form ability");
2905 if (this->getVectorToCursorFromScreenCentre().isLength2DIn(minMouse))
2907 debugLog("in and changing");
2908 if (dualFormMode == DUALFORM_NAIJA)
2909 dualFormMode = DUALFORM_LI;
2911 dualFormMode = DUALFORM_NAIJA;
2912 refreshDualFormModel();
2918 if (chargeLevelAttained == 2)
2920 if (dualFormMode == DUALFORM_NAIJA)
2921 dualFormMode = DUALFORM_LI;
2923 dualFormMode = DUALFORM_NAIJA;
2924 refreshDualFormModel();
2929 if (dsq->continuity.dualFormMode == Continuity::DUALFORM_NAIJA)
2932 // ~~~~~~~~~~ SOUL SCREAM
2934 if (chargeLevelAttained == 1)
2936 if (dsq->continuity.dualFormCharge >= requiredDualFormCharge)
2938 core->sound->playSfx("DualForm-Scream");
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));
2943 dsq->continuity.dualFormCharge = 0;
2944 dsq->shakeCamera(25, 2);
2946 core->globalScale = Vector(0.4, 0.4);
2947 myZoom = Vector(0.4, 0.4);
2950 setv(EV_NOINPUTNOVEL, 0);
2951 core->globalScale = Vector(1.5, 1.5);
2953 setv(EV_NOINPUTNOVEL, 1);
2960 if (e->getEntityType() == ET_ENEMY && e != this)
2962 if (e->isv(EV_SOULSCREAMRADIUS, -1) || (e->position - position).isLength2DIn(1000 + e->getv(EV_SOULSCREAMRADIUS)))
2966 d.damageType = DT_AVATAR_DUALFORMNAIJA;
2968 d.form = dsq->continuity.form;
2975 setv(EV_NOINPUTNOVEL, 0);
2977 dsq->screenTransition->capture();
2978 dsq->screenTransition->go(0.5);
2979 myZoom = Vector(1,1);
2980 setv(EV_NOINPUTNOVEL, 1);
2985 core->sound->playSfx("Denied");
2989 else if (dsq->continuity.dualFormMode == Continuity::DUALFORM_LI)
2991 if (chargeLevelAttained == 1)
2995 for (; i < num; i++)
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();
3003 s->setAimVector(v1*0.1 + v2*0.9);
3005 core->sound->playSfx("DualForm-Shot");
3006 dsq->spawnParticleEffect("DualFormFire", position);
3009 didShockDamage = false;
3010 doShock("DualFormLiTendril");
3015 core->sound->playSfx("Denied");
3019 if (fireAtNearestValidEntity("DualFormLi"))
3021 fireDelay = fireDelayTime;
3030 else if (ability == 1)
3041 if (chargeLevelAttained == 2)
3046 didShockDamage = false;
3048 didShockDamage = false;
3049 if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
3050 doShock("EnergyTendril2");
3052 doShock("EnergyTendril");
3053 if (!state.lockedToWall)
3054 skeletalSprite.animate("energyChargeAttack", 0, 6);
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));
3060 dsq->playVisualEffect(VFX_SHOCK, position, this);
3066 std::string shotName;
3067 if (dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY2))
3068 shotName = "EnergyBlast2";
3070 shotName = "EnergyBlast";
3072 if (fireAtNearestValidEntity(shotName))
3074 fireDelay = fireDelayTime;
3084 //debugLog("rock ability");
3087 if (formAbilityDelay == 0)
3089 formAbilityDelay = 0.2;
3090 //Vector pos = dsq->getGameCursorPosition() - position;
3092 Vector pos = getAim();
3094 pos.setLength2D(16);
3097 std::string seedName;
3098 if (chargeLevelAttained == 0)
3099 seedName = "SeedFlower";
3100 else if (chargeLevelAttained == 2)
3101 seedName = "SeedUberVine";
3103 Shot *s = dsq->game->fireShot(seedName, this, 0, pos, getAim());
3107 Vector pos = getAim();
3109 pos.setLength2D(64);
3113 //dsq->spawnParticleEffect("Fertilizer", pos);
3116 std::string seedName;
3117 if (chargeLevelAttained == 0)
3118 seedName = "SeedFlower";
3119 else if (chargeLevelAttained == 2)
3120 seedName = "SeedUberVine";
3122 e = dsq->game->createEntity(seedName, 0, pos, 0, false, "");
3124 Vector add = pos - position;
3125 add.setLength2D(800);
3130 if (chargeLevelAttained == 0)
3133 else if (chargeLevelAttained == 1)
3135 e->setState(STATE_CHARGE1);
3137 else if (chargeLevelAttained == 2)
3139 e->setState(STATE_CHARGE2);
3146 // something = charge2
3148 for (int i = 0; i < dsq->entities.size(); i++)
3150 Entity *e = dsq->entities[i];
3151 if (e && e->getEntityType() == ET_ENEMY && e->isDamageTarget(DT_AVATAR_NATURE))
3153 if ((e->position - pos).isLength2DIn(128))
3156 d.damageType = DT_AVATAR_NATURE;
3171 if (!dsq->continuity.isNaijaEatsEmpty())
3173 EatData *d = dsq->continuity.getLastNaijaEat();
3174 if (!d->shot.empty())
3176 int num = getNumShots()-2;
3178 for (int i = 0; i < num; i++)
3180 bool playSfx = true;
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)
3190 if (s->shotData->homing > 0)
3193 Vector p = dsq->getGameCursorPosition();
3194 target = dsq->game->getNearestEntity(p, 800, this, ET_ENEMY, s->shotData->damageType);
3203 s->position = bone_head->getWorldPosition();
3207 s->position = this->position;
3211 s->setAimVector(this->getVectorToCursor());
3214 Vector aim = this->getVectorToCursor();
3216 s->setAimVector(getTendrilAimVector(i, num)*0.1 + aim*0.9);
3219 if (s->shotData && s->shotData->avatarKickBack)
3221 Vector d = s->velocity;
3222 d.setLength2D(-s->shotData->avatarKickBack);
3224 if (!isUnderWater())
3226 push(d, s->shotData->avatarKickBackTime * effect, s->shotData->avatarKickBack * effect, 0);
3230 debugLog("firing: " + d->shot);
3234 dsq->continuity.removeLastNaijaEat();
3235 //dsq->continuity.removeEatData(eats.size()-1);
3245 if (tummyAmount <= 0)
3248 //fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000);
3260 if (fireAtNearestValidEntity("Vomit", inTummy, DT_AVATAR_VOMIT, 6000))
3269 Vector bitePos = position;
3270 Vector offset = vel;
3271 offset.setLength2D(128);
3274 for (int i = 0; i < dsq->entities.size(); i++)
3276 Entity *e = dsq->entities[i];
3278 if (e && (e->position - bitePos).getSquaredLength2D() < sqr(64))
3296 if (formAbilityDelay == 0 && chargeLevelAttained==1)
3298 core->sound->playSfx("SunForm");
3299 //dsq->spawnParticleEffect("LightFlare", position);
3301 chargeEmitter->load("SunFlare");
3302 chargeEmitter->start();
3304 PauseQuad *q = new PauseQuad;
3305 q->setTexture("Naija/LightFormGlow");
3306 q->position = position;
3307 q->setWidthHeight(1024, 1024);
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);
3318 if (e != this && (e->position - position).isLength2DIn(2048))
3323 //formAbilityDelay = 0.1;
3329 // absorbs nearby shots, and respawns the player if in a "SPIRITBEACON" node
3330 if (formAbilityDelay == 0)
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++)
3339 if (s->shotData && s->firer)
3341 if (!s->shotData->invisible && s->firer->getEntityType()==ET_ENEMY)
3343 if ((s->position - position).isLength2DIn(256))
3346 delShots.push_back(s);
3347 spiritEnergyAbsorbed++;
3352 for (std::list<Shot*>::iterator j = delShots.begin(); j != delShots.end(); j++)
3357 if (spiritEnergyAbsorbed > 4)
3359 dsq->game->spawnManaBall(position, 1);
3360 spiritEnergyAbsorbed = 0;
3362 spiritBeaconEmitter.start();
3363 formAbilityDelay = 1.0;
3365 Path *p = dsq->game->getNearestPath(position, "SPIRITBEACON");
3366 if (p && p->isCoordinateInside(position))
3368 bodyPosition = position;
3371 pullTarget->position = position;
3377 Path *p = dsq->game->getNearestPath(position, PATH_SPIRITPORTAL);
3378 if (p && p->isCoordinateInside(position))
3381 changeForm(FORM_NORMAL);
3382 dsq->game->warpToSceneFromNode(p);
3395 Vector Avatar::getTendrilAimVector(int i, int max)
3397 float a = float(float(i)/float(max))*3.14*2;
3398 Vector aim(sinf(a), cosf(a));
3399 if (state.lockedToWall)
3401 Vector n = dsq->game->getWallNormal(position);
3404 aim = aim*0.4 + n*0.6;
3410 int Avatar::getNumShots()
3412 int thits = normalTendrilHits;
3413 if (flourishPowerTimer.isActive())
3415 if (lastBurstType == BURST_WALL)
3416 thits = maxTendrilHits;
3418 thits = rollTendrilHits;
3424 if (lastBurstType == BURST_WALL)
3425 thits = rollTendrilHits;
3431 void Avatar::doShock(const std::string &shotName)
3437 //int maxHit = 2 + dsq->continuity.getSpellLevel(SPELL_SHOCK)*2;
3439 std::vector <Entity*> entitiesToHit;
3440 std::vector <Target> localTargets;
3441 bool clearTargets = true;
3443 int thits = getNumShots();
3446 if (skeletalSprite.getAnimationLayer(LAYER_FLOURISH)->getCurrentAnimation())
3448 thits = maxTendrilHits;
3452 if (!targets.empty() && targets[0].e != 0)
3454 clearTargets = false;
3455 for (int i = 0; i < thits; i++)
3457 entitiesToHit.push_back(targets[0].e);
3462 //std::vector <Target> localTargets;
3464 localTargets.clear();
3467 EntityList entityList;
3471 if (e != this && e->isPresent() && e->isDamageTarget(DT_AVATAR_SHOCK) && (e->position - position).isLength2DIn(range))
3473 entityList.push_back(e);
3479 Target t = getNearestTarget(position, position, this, DT_AVATAR_SHOCK, true, &localTargets, &entityList);
3482 localTargets.push_back(t);
3483 entitiesToHit.push_back(t.e);
3484 targets.push_back(t);
3493 if (!localTargets.empty())
3495 while (entitiesToHit.size()<thits)
3497 for (int i = 0; i < localTargets.size(); i++)
3499 if (!(entitiesToHit.size()<thits))
3501 entitiesToHit.push_back(localTargets[i].e);
3502 targets.push_back(localTargets[i]);
3506 localTargets.clear();
3509 Vector aim = getAim();
3511 int sz = entitiesToHit.size();
3512 float spread = 3.14;
3519 for (int i = 0; i < thits; i++)
3521 Shot *s = dsq->game->fireShot(shotName, this, 0);
3523 s->setAimVector(getTendrilAimVector(i, thits));
3525 checkUpgradeForShot(s);
3530 for (int i = 0; i < sz; i++)
3532 Entity *e = entitiesToHit[i];
3535 Shot *s = dsq->game->fireShot(shotName, this, e);
3536 if (!targets.empty())
3538 for (int j = 0; j < targets.size(); j++)
3540 if (targets[j].e == e)
3541 s->targetPt = targets[j].targetPt;
3545 else if (!localTargets.empty())
3547 for (int j = 0; j < localTargets.size(); j++)
3549 if (localTargets[j].e == e)
3550 s->targetPt = localTargets[j].targetPt;
3554 Vector d = e->position - position;
3556 float a = float(float(i)/float(sz))*3.14*2;
3557 Vector aim(sinf(a), cosf(a));
3559 swizzleTendrilAimVector(aim);
3561 s->setAimVector(getTendrilAimVector(i, thits));
3562 checkUpgradeForShot(s);
3565 MathFunctions::calculateAngleBetweenVectorsInRadians(Vector(0,-1), d, ang);
3567 Vector adjust = a*spread + ang;
3569 s->setAimVector((d + adjust)/2);
3583 for (int i = 0; i < entitiesToHit.size(); i++)
3585 Entity *e = entitiesToHit[i];
3588 d.damageType = DT_AVATAR_SHOCK;
3592 dsq->playVisualEffect(VFX_SHOCKHIT, e->position, e);
3594 EnergyTendril *t = new EnergyTendril(this, e);
3595 core->addRenderObject(t, LR_PARTICLES);
3601 //HACK: WHAT DOES THIS VARIABLE DO EXACTLY?
3602 didShockDamage = true;
3608 void Avatar::updateShock(float dt)
3613 float shockTime = 0.75;
3614 castShockTimer += dt;
3615 std::vector<Entity*> closestEntities;
3617 const float shotDelayTime = 0.05;
3620 if (damageDelay > 0)
3623 if (damageDelay <= 0)
3627 if (damageDelay == 0)
3631 if (damageDelay == 0)
3645 void Avatar::formAbilityUpdate(float dt)
3647 switch(dsq->continuity.form)
3651 if (core->mouse.buttons.right)
3653 const float bubbleRate = 0.2;
3655 state.abilityDelay -= dt;
3656 if (state.abilityDelay < 0)
3657 state.abilityDelay = 0;
3659 if (state.abilityDelay == 0)
3661 state.abilityDelay = bubbleRate;
3662 //state.abilityDelay -= bubbleRate;
3664 //Entity *bubble = dsq->game->createEntity("FishFormBubble", 0, position, 0, false, "");
3665 Vector dir = getAim();
3668 dsq->game->fireShot("FishFormBubble", this, 0, position+dir*16, dir);
3676 if (core->mouse.buttons.right && mana > 0 && !core->mouse.buttons.left)
3678 float shockTime = 0.75;
3679 castShockTimer += dt;
3680 std::vector<Entity*> closestEntities;
3682 const float shotDelayTime = 0.05;
3685 int maxHit = 2 + dsq->continuity.getSpellLevel(SPELL_SHOCK)*2;
3686 for (int i = 0; i < dsq->entities.size(); i++)
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))
3692 state.shotDelay += dt;
3693 if (state.shotDelay > shotDelayTime)
3695 state.shotDelay -= shotDelayTime;
3697 EnergyTendril *t = new EnergyTendril(avatar->position, e->position);
3698 core->addRenderObject(t, LR_PARTICLES);
3700 e->offset.x = rand()%5;
3704 d.spellType = SPELL_SHOCK;
3705 d.damage = 1+(dsq->continuity.getSpellLevel(SPELL_SHOCK)-1)*1;
3712 //std::ostringstream os;
3713 //os << "castShockTimer: " << castShockTimer << " - shockTime: " << shockTime;
3714 //debugLog(os.str());
3715 if (castShockTimer > shockTime)
3717 castShockTimer -= shockTime;
3723 state.shotDelay = 0;
3732 bool Avatar::isMouseInputEnabled()
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;
3742 void Avatar::rmbd2()
3751 //core->setDockIcon("BitBlot");
3752 if (!isMouseInputEnabled() || isEntityDead()) return;
3753 if (dsq->continuity.form == FORM_NORMAL )
3755 //if (isCoordinateInRadius(dsq->getGameCursorPosition(), 96))
3756 ///Vector diff = core->mouse.position - c;
3757 if (dsq->inputMode == INPUT_MOUSE && !rmb_flag)
3759 Vector diff = getVectorToCursorFromScreenCentre();
3760 if (diff.getSquaredLength2D() < sqr(openSingingInterfaceRadius))
3761 openSingingInterface();
3765 openSingingInterface();
3770 if (spellCastDelay == 0)
3774 if (spellCastDelay == 0)
3781 if (!isMouseInputEnabled() || isEntityDead()) return;
3785 if (!entityToActivate && !pathToActivate)
3792 dsq->cursorGlow->alpha.interpolateTo(0, 0.2);
3793 dsq->cursorBlinker->alpha.interpolateTo(0, 0.2);
3795 if (pickingPullTarget)
3797 if (potentialPullTarget)
3799 pullTarget = potentialPullTarget;
3800 debugLog("Calling start pull");
3801 pullTarget->startPull();
3803 closePullTargetInterface();
3808 closeSingingInterface();
3812 if (entityToActivate)
3814 activateEntity = entityToActivate;
3815 entityToActivate = 0;
3819 pathToActivate->activate();
3834 bool Avatar::canCharge(int ability)
3836 switch(dsq->continuity.form)
3839 if (ability == 0) return true;
3842 //if (inTummy) return true;
3846 if (dualFormMode == DUALFORM_NAIJA)
3848 if (dualFormCharge >= requiredDualFormCharge)
3871 void Avatar::startCharge(int ability)
3873 if (!isCharging() && canCharge(ability))
3875 if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
3877 core->sound->stopSfx(dsq->loops.charge);
3878 dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
3882 sfx.name = "ChargeLoop";
3884 dsq->loops.charge = core->sound->playSfx(sfx);
3886 state.spellCharge = 0;
3888 chargeLevelAttained = 0;
3891 chargeGraphic->alpha = 0;
3892 chargeGraphic->scale = Vector(0,0);
3893 chargeGraphic->alpha.interpolateTo(0.6, chargeMax, 0);
3895 chargeGraphic->scale.interpolateTo(Vector(sz,sz), chargeMax, 0);
3898 switch(dsq->continuity.form)
3901 chargingEmitter->load("ChargingEnergy");
3904 chargingEmitter->load("ChargingNature");
3907 chargingEmitter->load("ChargingEnergy");
3910 chargingEmitter->load("ChargingDualForm");
3913 chargingEmitter->load("ChargingGeneric");
3917 chargingEmitter->start();
3920 abilityCharging = ability;
3923 if (!canCharge(ability))
3925 formAbility(ability);
3929 void Avatar::setBlockSinging(bool v)
3934 bool Avatar::canSetBoneLock()
3937 if (dsq->continuity.form == FORM_FISH || dsq->continuity.form == FORM_SPIRIT)
3944 void Avatar::onSetBoneLock()
3946 Entity::onSetBoneLock();
3950 skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
3952 state.lockedToWall = 1;
3953 wallNormal = boneLock.localOffset;
3954 wallNormal.normalize2D();
3955 rotateToVec(wallNormal, 0.1);
3959 if (state.lockedToWall)
3966 void Avatar::onUpdateBoneLock()
3968 Entity::onUpdateBoneLock();
3970 wallNormal = boneLock.wallNormal;
3971 rotateToVec(wallNormal, 0.01);
3976 if (!isMouseInputEnabled()) return;
3981 Vector v = getVectorToCursor();
3982 if (v.isLength2DIn(getStopDistance()) && !v.isLength2DIn(minMouse))
3984 if (state.lockedToWall)
3993 if (spellCastDelay == 0)
3999 void Avatar::fallOffWall()
4001 //stillTimer.stop();
4002 if (state.lockedToWall)
4004 lockToWallFallTimer = 0;
4005 state.nearWall = false;
4006 state.lockedToWall = false;
4008 setBoneLock(BoneLock());
4012 offset.interpolateTo(Vector(0,0), 0.1);
4013 if (!wallNormal.isZero())
4015 Vector velSet = wallNormal;
4016 velSet.setLength2D(200);
4019 //doCollisionAvoidance(dt, 5, 1);
4025 if (!isMouseInputEnabled()) return;
4027 if (dsq->continuity.toggleMoveMode)
4028 movingOn = !movingOn;
4036 bool Avatar::isCharging()
4041 void Avatar::endCharge()
4045 if (dsq->loops.charge != BBGE_AUDIO_NOCHANNEL)
4047 core->sound->stopSfx(dsq->loops.charge);
4048 dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
4053 std::ostringstream os;
4054 os << "spellCharge: " << spellCharge;
4058 chargeGraphic->alpha.interpolateTo(0, 0.5, 0);
4059 chargeGraphic->scale.interpolateTo(Vector(0,0), 1.0, 0);
4062 chargingEmitter->stop();
4065 state.spellCharge = 0;
4069 Vector Avatar::getWallNormal(TileVector t)
4071 return dsq->game->getWallNormal(t.worldVector(), 5)*-1;
4076 for (int x = -2; x <= 2; x++)
4078 for (int y = -2; y <= 2; y++)
4080 TileVector check = t;
4083 if (!dsq->game->isObstructed(check))
4085 Vector v(check.x, check.y);
4089 //check.x+x, check.y+y
4095 //accum.normalize2D();
4096 accum.setLength2D(-1);
4105 int Avatar::getSingingInterfaceRadius()
4107 return singingInterfaceRadius;
4110 int Avatar::getOpenSingingInterfaceRadius()
4112 return openSingingInterfaceRadius;
4115 bool Avatar::isSwimming()
4120 void Avatar::lockToWallCommon()
4124 skeletalSprite.stopAllAnimations();
4125 rotationOffset.interpolateTo(0, 0.01);
4127 fallGravityTimer = 0;
4129 dsq->spawnParticleEffect("LockToWall", position);
4132 disableOverideMaxSpeed();
4133 core->sound->playSfx("LockToWall", 1.0, 0);//, (1000+rand()%100)/1000.0);
4135 animatedBurst = false;
4137 //lastLockToWallPos = position;
4139 state.lockToWallDelay.start(0.2);
4140 state.lockedToWall = true;
4142 lockToWallFallTimer = -1;
4144 // move this to its own function?
4145 state.backFlip = false;
4146 skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
4149 void Avatar::lockToWall()
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;
4159 Vector opos = position;
4160 position = lastPosition;
4163 TileVector t(position);
4164 TileVector myTile = t;
4178 TileVector tback = myTile;
4179 tback.x += int(m.x);
4180 tback.y += int(m.y);
4184 TileVector tnext = myTile;
4185 tnext.x += int(add.x);
4186 tnext.y += int(add.y);
4188 // find the fraking wall
4190 TileVector actualWall = myTile;
4192 Vector getWall(myTile.x, myTile.y);
4193 for (int i = -1; i < 5; i++)
4195 getWall.x += add.x*i;
4196 getWall.y += add.y*i;
4197 if (lastWall.isZero())
4199 TileVector test(getWall.x, getWall.y);
4200 if (dsq->game->isObstructed(test))
4204 actualWall = TileVector(lastWall.x, lastWall.y);
4207 Vector diff = lastLockToWallPos - position;
4210 if (!dsq->game->isObstructed(t))
4218 test = TileVector(t.x, t.y+1);
4219 if (dsq->game->isObstructed(test))
4224 test = TileVector(t.x, t.y-1);
4225 if (dsq->game->isObstructed(test))
4230 test = TileVector(t.x-1, t.y);
4231 if (dsq->game->isObstructed(test))
4236 test = TileVector(t.x+1, t.y);
4237 if (dsq->game->isObstructed(test))
4242 test = TileVector(t.x+1, t.y+1);
4243 if (dsq->game->isObstructed(test))
4248 test = TileVector(t.x-1, t.y+1);
4249 if (dsq->game->isObstructed(test))
4254 test = TileVector(t.x+1, t.y-1);
4255 if (dsq->game->isObstructed(test))
4260 test = TileVector(t.x-1, t.y-1);
4261 if (dsq->game->isObstructed(test))
4275 //debugLog("trying other");
4282 if (dsq->game->getGrid(t)==OT_HURT && dsq->continuity.form != FORM_NATURE)
4286 if (good /*&& dsq->game->)isObstructed(t2, OT_BLACK)*/ /*&& diff.getSquaredLength2D() > sqr(40)*/)
4288 wallNormal = dsq->game->getWallNormal(position);
4289 bool outOfWaterHit = (!_isUnderWater && !(wallNormal.y < -0.1));
4290 if (wallNormal.isZero() ) //|| outOfWaterHit
4292 debugLog("COULD NOT FIND NORMAL, GOING TO BOUNCE");
4296 Animation *anim = skeletalSprite.getCurrentAnimation();
4297 if (anim && anim->name == "hitGround")
4302 skeletalSprite.animate("hitGround");
4312 position = TileVector(position).worldVector();
4313 lastPosition = position;
4316 if (!dsq->mod.isActive() && !dsq->continuity.getFlag("lockedToWall"))
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);
4328 lockToWallFallTimer = 0.4;
4330 lockToWallFallTimer = -1;
4332 //wallPushVec = getWallNormal(t);
4335 wallPushVec = wallNormal;
4336 wallPushVec *= 2000;
4338 skeletalSprite.stopAllAnimations();
4339 if (wallPushVec.y < 0 && (fabs(wallPushVec.y) > fabs(wallPushVec.x)))
4341 skeletalSprite.transitionAnimate("wallLookUp", 0.2, -1);
4345 skeletalSprite.transitionAnimate("wall", 0.2, -1);
4347 rotateToVec(wallPushVec, 0.1);
4354 if (!dsq->game->isObstructed(tnext))
4361 int tileType = dsq->game->getGrid(t);
4362 Vector offdiff = t.worldVector() - position;
4363 if (!offdiff.isZero())
4365 if (tileType != OT_INVISIBLEIN)
4367 Vector adjust = offdiff;
4368 adjust.setLength2D(TILE_SIZE*2);
4373 Vector adjust = offdiff;
4374 adjust.setLength2D(TILE_SIZE/2);
4379 float spd = vel.getLength2D();
4383 Vector diff = offset - offdiff;
4384 float len = diff.getLength2D();
4391 std::ostringstream os;
4392 os << "time: " << time;
4395 offset.interpolateTo(offdiff, time);
4397 if (tileType == OT_INVISIBLEIN)
4400 goIn.setLength2D(-28);
4404 Vector diff = uset.worldVector()-position;
4407 offset.interpolateTo(goIn, 0.05);
4412 vel = Vector(0,0,0);
4416 Vector oldPos = position;
4423 TileVector t(position);
4424 if (dsq->game->isObstructed(t, OT_BLACK))
4435 //debugLog("COULD NOT FIND TILE TO GRAB ONTO");
4440 void Avatar::applyTripEffects()
4442 color.interpolateTo(BLIND_COLOR, 0.5);
4443 currentColor = BLIND_COLOR;
4445 tripper->alpha.interpolateTo(1, 8);
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);
4453 if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
4455 dsq->sound->stopSfx(dsq->loops.trip);
4456 dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
4460 play.name = "TripLoop";
4464 dsq->loops.trip = dsq->sound->playSfx(play);
4467 void Avatar::removeTripEffects()
4469 color.interpolateTo(Vector(1,1,1),0.5);
4470 currentColor = Vector(1,1,1);
4471 tripper->alpha.interpolateTo(0, 4);
4473 if (dsq->loops.trip != BBGE_AUDIO_NOCHANNEL)
4475 dsq->sound->fadeSfx(dsq->loops.trip, SFT_OUT, 3);
4476 dsq->loops.trip = BBGE_AUDIO_NOCHANNEL;
4480 void Avatar::applyBlindEffects()
4485 color.interpolateTo(BLIND_COLOR, 0.5);
4486 currentColor = BLIND_COLOR;
4487 blinder->alpha.interpolateTo(1, 0.5);
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);
4494 //dsq->toggleMuffleSound(1);
4497 void Avatar::removeBlindEffects()
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);
4505 void Avatar::setBlind(float time)
4509 removeBlindEffects();
4515 applyBlindEffects();
4518 if (time > state.blindTimer.getValue())
4519 ///*state.blindTimer.getValue() + */
4520 state.blindTimer.start(time);
4523 void Avatar::setNearestPullTarget()
4525 int smallestDist = -1;
4527 Entity *closest = 0;
4533 if ((e->isPullable()) && e->life == 1)
4535 float dist = (e->position - position).getSquaredLength2D();
4536 if (dist < sqr(maxDist) && (smallestDist == -1 || dist < smallestDist))
4539 smallestDist = dist;
4546 pullTarget = closest;
4547 pullTarget->startPull();
4551 void Avatar::openPullTargetInterface()
4553 debugLog("Open pull target");
4556 pullTarget->stopPull();
4559 potentialPullTarget = 0;
4560 pickingPullTarget = true;
4561 // change the cursor
4562 dsq->cursor->color = Vector(0.5,0.5,1);
4565 void Avatar::closePullTargetInterface()
4567 debugLog("close pull target");
4568 pickingPullTarget = false;
4569 potentialPullTarget = 0;
4570 dsq->cursor->color = Vector(1,1,1);
4573 void Avatar::createWeb()
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);
4582 void Avatar::clearWeb()
4586 web->setExistence(25);
4588 //web->setDecayRate(1.0f/30.0f);
4589 //web->fadeAlphaWithLife = 1;
4595 Avatar::Avatar() : Entity(), ActionMapper()
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);
4613 lastBurstType = BURST_NONE;
4614 dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
4615 leftHandEmitter = rightHandEmitter = 0;
4616 boneLeftHand = boneRightHand = 0;
4617 canChangeForm = true;
4619 dsq->loops.charge = BBGE_AUDIO_NOCHANNEL;
4623 headTextureTimer = 0;
4624 bone_dualFormGlow = 0;
4625 //dsq->continuity.dualFormCharge = 0;
4626 //dsq->continuity.dualFormMode = Continuity::DUALFORM_NAIJA;
4627 debugLog("Avatar 1");
4629 //registerEntityDied = true;
4630 setv(EV_ENTITYDIED, 1);
4634 invincibleBreak = true;
4635 targetUpdateDelay = 0;
4637 songInterfaceTimer = 0;
4638 quickSongCastDelay = 0;
4642 blockSinging = false;
4645 spiritEnergyAbsorbed = 0;
4646 joystickMove = false;
4648 debugLog("setCanLeaveWater");
4650 setCanLeaveWater(true);
4652 debugLog("setOverrideRenderPass");
4654 setOverrideRenderPass(1);
4656 debugLog("Done those");
4662 fallGravityTimer = 0;
4663 lastOutOfWaterMaxSpeed = 0;
4664 //chargeGraphic = 0;
4667 ropeTimer = shieldPoints = auraTimer = 0;
4671 inFormInterface = false;
4676 lastQuad = lastQuadDir = rollDelay = rolling = 0;
4678 doubleClickDelay = 0;
4680 didShockDamage = false;
4681 chargeLevelAttained = 0;
4683 activeAura = AURA_NONE;
4686 currentMaxSpeed = 0;
4687 abilityCharging = -1;
4688 pickingPullTarget = false;
4689 potentialPullTarget = 0;
4692 currentSongIdx = -1;
4695 debugLog("Avatar vars->");
4697 damageTime = vars->avatarDamageTime;
4702 //scale = Vector(0.5, 0.5);
4703 scale = Vector(0.5, 0.5);
4705 debugLog("Avatar 2");
4706 //scale = Vector(1.0, 1.0);
4707 //setTexture("Naija-sprite2");
4710 setEntityType(ET_AVATAR);
4715 lastEntityActivation = 0;
4717 entityToActivate = 0;
4719 zoomOverriden = false;
4721 disableConversationStart = false;
4725 myZoom = Vector(1,1);
4726 spellChargeMin = spellCastDelay = 0;
4727 this->pushingOffWallEffect = 0;
4728 lockToWallFallTimer = 0;
4733 animatedBurst = false;
4736 ignoreInputDelay = 0;
4746 debugLog("Avatar 3");
4748 hair->setTexture("Naija/Cape");
4749 hair->setOverrideRenderPass(1);
4750 hair->setRenderPass(1);
4751 dsq->game->addRenderObject(hair, LR_ENTITIES);
4753 debugLog("Avatar 4");
4757 debugLog("Avatar 5");
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;
4769 dsq->game->addRenderObject(blinder, LR_AFTER_EFFECTS);
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;
4780 dsq->game->addRenderObject(tripper, LR_AFTER_EFFECTS);
4782 songIcons.resize(8);
4784 for (i = 0; i < songIcons.size(); i++)
4786 songIcons[i] = new SongIcon(i);
4787 songIcons[i]->alpha = 0;
4788 songIcons[i]->followCamera = 1;
4789 dsq->game->addRenderObject(songIcons[i], LR_HUD);
4792 setSongIconPositions();
4795 fader->position = Vector(400,300);
4796 fader->setTexture("fader");
4797 fader->setWidthHeight(core->getVirtualWidth()+10);
4798 fader->followCamera = 1;
4800 dsq->game->addRenderObject(fader, LR_AFTER_EFFECTS);
4807 chargeGraphic = new Particle;
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);
4822 //skeletalSprite.getBoneByIdx(3)->addChild(chargeGraphic);
4825 debugLog("Avatar 6");
4827 targetQuads.resize(targets.size());
4829 for (i = 0; i < targets.size(); i++)
4831 targetQuads[i] = new ParticleEffect;
4833 targetQuads[i]->setTexture("missingImage");
4834 targetQuads[i]->alpha = 0;
4836 targetQuads[i]->load("EnergyBlastTarget");
4838 // HACK: should have its own layer?
4839 dsq->game->addRenderObject(targetQuads[i], LR_PARTICLES);
4842 lightFormGlow = new Quad("Naija/LightFormGlow", 0);
4843 lightFormGlow->alpha = 0;
4845 lightFormGlow->scale.interpolateTo(Vector(5.5, 5.5), 0.4, -1, 1);
4846 //lightFormGlow->positionSnapTo = &position;
4847 dsq->game->addRenderObject(lightFormGlow, LR_ELEMENTS13);
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);
4855 debugLog("Avatar 7");
4857 addChild(®enEmitter, PM_STATIC);
4858 regenEmitter.load("FoodEffectRegen");
4860 addChild(&speedEmitter, PM_STATIC);
4861 speedEmitter.load("FoodEffectSpeed");
4863 addChild(&defenseEmitter, PM_STATIC);
4864 defenseEmitter.load("FoodEffectDefense");
4866 addChild(&invincibleEmitter, PM_STATIC);
4867 invincibleEmitter.load("FoodEffectInvincible");
4869 addChild(&auraEmitter, PM_STATIC);
4871 addChild(&auraHitEmitter, PM_STATIC);
4872 auraHitEmitter.load("AuraShieldHit");
4874 chargingEmitter = new ParticleEffect;
4875 dsq->getTopStateData()->addRenderObject(chargingEmitter, LR_PARTICLES);
4877 addChild(&chargingEmitter);
4878 chargingEmitter.parentManagedStatic = true;
4881 chargeEmitter = new ParticleEffect;
4882 dsq->getTopStateData()->addRenderObject(chargeEmitter, LR_PARTICLES_TOP);
4884 leftHandEmitter = new ParticleEffect;
4885 dsq->getTopStateData()->addRenderObject(leftHandEmitter, LR_PARTICLES);
4887 rightHandEmitter = new ParticleEffect;
4888 dsq->getTopStateData()->addRenderObject(rightHandEmitter, LR_PARTICLES);
4891 leftHandEmitter = new ParticleEffect;
4892 dsq->getTopStateData()->addRenderObject(`, LR_PARTICLES);
4894 rightHandEmitter = new ParticlesEffect;
4895 dsq->getTopStateData()->addRenderObject(rightHandEmitter, LR_PARTICLES);
4899 addChild(&chargeEmitter);
4900 chargeEmitter.parentManagedStatic = true;
4903 addChild(&biteLeftEmitter, PM_STATIC);
4904 biteLeftEmitter.load("BiteLeft");
4906 addChild(&biteRightEmitter, PM_STATIC);
4907 biteRightEmitter.load("BiteRight");
4909 addChild(&wakeEmitter, PM_STATIC);
4910 wakeEmitter.load("Wake");
4912 addChild(&swimEmitter, PM_STATIC);
4913 swimEmitter.load("Swim");
4915 addChild(&plungeEmitter, PM_STATIC);
4916 plungeEmitter.load("Plunge");
4917 plungeEmitter.position = Vector(0,-100);
4919 addChild(&spiritBeaconEmitter, PM_STATIC);
4920 spiritBeaconEmitter.load("SpiritBeacon");
4922 addChild(&healEmitter, PM_STATIC);
4924 addChild(&hitEmitter, PM_STATIC);
4926 addChild(&rollLeftEmitter, PM_STATIC);
4928 addChild(&rollRightEmitter, PM_STATIC);
4930 rollRightEmitter.load("RollRight");
4931 rollLeftEmitter.load("RollLeft");
4933 debugLog("Avatar 8");
4935 perform(STATE_IDLE);
4936 skeletalSprite.animate(getIdleAnimName(),-1);
4938 debugLog("Avatar 9");
4942 debugLog("useMic...initing recording");
4945 debugLog("RecordStart...");
4946 avatarRecord = BASS_RecordStart(44100,1,0,&recordCallback,0);
4950 if (dsq->autoSingMenuOpen)
4955 debugLog("ChannelPause...");
4956 BASS_ChannelPause(avatarRecord);
4959 debugLog("...done");
4963 debugLog("Avatar 10");
4964 setDamageTarget(DT_AVATAR_LANCE, false);
4966 //changeForm(FORM_NORMAL, false);
4968 refreshNormalForm();
4972 void Avatar::revert()
4976 if (dsq->continuity.form != FORM_NORMAL)
4977 changeForm(FORM_NORMAL);
4981 void Avatar::onHeal(int type)
4985 healEmitter.load("Heal");
4986 healEmitter.start();
4990 void Avatar::refreshNormalForm()
4992 std::string c = dsq->continuity.costume;
4995 refreshModel("Naija", c);
4999 hair->alphaMod = 1.0;
5000 if (!c.empty() && c!="Naija")
5002 if (exists(core->getBaseTextureDirectory() + "naija/cape-"+c+".png"))
5005 hair->setTexture("naija/cape-"+c);
5016 hair->setTexture("naija/cape");
5022 hair->alphaMod = 0.0;
5026 void Avatar::refreshModel(std::string file, const std::string &skin, bool forceIdle)
5028 stringToLower(file);
5030 bool loadedSkeletal = false;
5032 if (!skeletalSprite.isLoaded() || nocasecmp(skeletalSprite.filenameLoaded, file)!=0)
5034 skeletalSprite.loadSkeletal(file);
5035 loadedSkeletal = true;
5038 skeletalSprite.loadSkin(skin);
5040 if (file == "beast")
5042 skeletalSprite.scale = Vector(1.25,1.25);
5045 skeletalSprite.scale = Vector(1,1);
5047 Animation *anim = skeletalSprite.getCurrentAnimation(0);
5048 if (forceIdle || (!anim || loadedSkeletal))
5053 if (file == "naija")
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);
5066 boneLeftHand = skeletalSprite.getBoneByName("LeftArm");
5067 boneRightHand = skeletalSprite.getBoneByName("RightArm");
5071 bone_dualFormGlow = 0;
5073 boneRightFoot = boneLeftFoot = boneRightArm = boneLeftArm = boneFish2 = skeletalSprite.getBoneByIdx(0);
5074 boneLeftHand = boneRightHand = 0;
5079 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
5087 void Avatar::destroy()
5093 if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
5095 core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
5096 dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
5098 if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
5100 core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
5101 dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
5105 void Avatar::fireRope()
5109 if (!ropeVel.isLength2DIn(1))
5114 //ropeVel = core->mouse.position - Vector(400,300);
5116 ropeVel.setLength2D(7000);
5120 ropeVel = Vector(0,0,0);
5123 void Avatar::toggleZoom()
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);
5136 else if (core->globalScale.x == 1.5)
5137 core->globalScale.interpolateTo(Vector(1,1),0.2);
5143 void Avatar::setActiveSpell(Spells spell)
5145 activeSpell = spell;
5149 void Avatar::dodge(std::string dir)
5151 if (bursting) return;
5152 if (!canMove) return;
5153 if (dodgeDelay == 0)
5159 else if (dir == "left")
5160 mov = Vector(-1, 0);
5161 else if (dir == "down")
5163 else if (dir == "up")
5164 mov = Vector(0, -1);
5166 Vector lastPosition = position;
5167 //position += mov * 80;
5169 dodgeVec = mov * 8000;
5170 vel += mov * vars->maxDodgeSpeed;
5171 //dodgeEffectTimer = 0.125;
5172 state.dodgeEffectTimer.start(/*0.125*/vars->dodgeTime);
5174 float vlen = vel.getLength2D();
5179 if (dsq->game->collideCircleWithGrid(position, 24))
5181 position = lastPosition;
5187 void Avatar::startBackFlip()
5189 if (boneLock.on) return;
5192 skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip", 0.2, 0);
5193 vel.x = -vel.x*0.25;
5194 state.backFlip = true;
5197 void Avatar::stopBackFlip()
5201 //skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->stopAnimation();
5202 skeletalSprite.getAnimationLayer(ANIMLAYER_OVERRIDE)->transitionAnimate("backflip2", 0.2, 0);
5203 state.backFlip = false;
5207 void Avatar::startBurstCommon()
5209 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
5211 bittenEntities.clear();
5213 if (dsq->continuity.form == FORM_BEAST)
5215 setHeadTexture("Bite");
5219 setBoneLock(BoneLock());
5223 if (dsq->continuity.form == FORM_BEAST)
5226 biteRightEmitter.start();
5228 biteLeftEmitter.start();
5232 void Avatar::startBurst()
5234 //getVectorToCursorFromScreenCentre()
5235 //!bursting && burst == 1
5238 bool nearWallProblem = false;
5239 if (vel.isLength2DIn(200))
5243 nearWallProblem = false
5247 nearWallProblem = true;
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))
5256 if (!bursting && burst == 1)
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);
5268 lastBurstType = BURST_NORMAL;
5270 else if (bursting && burstTimer > 0.3)
5272 if (!flourish && !state.nearWall)
5273 //&& dsq->continuity.form == FORM_NORMAL)
5275 //if (rand()%100 < 50)
5283 skeletalSprite.transitionAnimate("flourish2", 0.1, 0, 3);
5286 rotationOffset = Vector(0,0,-360);
5288 rotationOffset = Vector(0,0,360);
5289 rotationOffset.interpolateTo(Vector(0,0,0), 0.8, 0, 0, 1);
5296 add.setLength2D(50);
5304 void Avatar::startWallBurst(bool useCursor)
5306 //if (!bursting && burst == 1 )
5310 //goDir = getVectorToCursorFromScreenCentre();
5311 goDir = getVectorToCursor();
5313 if (goDir.isLength2DIn(BURST_DISTANCE))
5315 if (!goDir.isLength2DIn(minMouse))
5319 goDir.normalize2D();
5321 if (_isUnderWater && dsq->continuity.form != FORM_BEAST)
5322 wakeEmitter.start();
5324 offset.interpolateTo(Vector(0,0), 0.05);
5326 dsq->spawnParticleEffect("WallBoost", position+offset, rotation.z);
5327 if (goDir.x != 0 || goDir.y != 0)
5329 lastBurstType = BURST_WALL;
5331 if (wallBurstTimer > 0)
5333 Vector wallJumpDir = position - lastWallJumpPos;
5334 if (lastWallJumpPos.isZero() || wallJumpDir.dot2D(lastWallJumpPos) > 0.2)
5336 std::ostringstream os;
5337 os << "wallJumps: " << wallJumps;
5343 debugLog("failed angle");
5347 wallBurstTimer = 0.8 - 0.1 * wallJumps;
5348 if (wallBurstTimer <= 0)
5351 debugLog("super boost!");
5356 lastWallJumpPos = position;
5357 lastWallJumpDir = position - lastWallJumpPos;
5360 dsq->rumble(0.22, 0.22, 0.2);
5361 bittenEntities.clear();
5364 wallPushVec = (goDir*0.75 + wallNormal*0.25);
5365 //wallPushVec = goDir;
5369 float v = goDir.dot2D(wallNormal);
5371 wallPushVec = wallNormal;
5374 wallPushVec = (goDir*0.5 + wallNormal*0.5);
5377 //wallPushVec = (goDir*0.9 + wallNormal*0.1);
5378 wallPushVec.setLength2D(vars->maxWallJumpBurstSpeed);
5381 pushingOffWallEffect = 0.5;
5383 this->state.lockedToWall = false;
5384 skeletalSprite.stopAllAnimations();
5387 dsq->sound->playSfx("WallJump", 255, 0, 1000+wallJumps*100);
5389 dsq->game->playBurstSound(pushingOffWallEffect>0);
5390 skeletalSprite.animate(getBurstAnimName(), 0);
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));
5403 float len = wallPushVec.getLength2D();
5407 Vector test = goDir;
5408 test |= TILE_SIZE*2;
5409 if (dsq->game->isObstructed(TileVector(position + test)))
5413 wallPushVec = goDir;
5416 wallPushVec = Vector((wallPushVec.x+goDir.x)/2, (wallPushVec.y+goDir.y)/2);
5422 //wallPushVec |= 10;
5427 void Avatar::doDodgeInput(const std::string &action, int s)
5434 state.tapTimer.start(0.25);
5436 else if (tapped == action)
5438 if (state.tapTimer.isActive())
5450 Vector Avatar::getKeyDir()
5453 if (isActing(ACTION_SWIMLEFT))
5454 dir += Vector(-1,0);
5455 if (isActing(ACTION_SWIMRIGHT))
5457 if (isActing(ACTION_SWIMUP))
5458 dir += Vector(0,-1);
5459 if (isActing(ACTION_SWIMDOWN))
5462 if (dir.x != 0 && dir.y != 0)
5468 Vector Avatar::getVectorToCursorFromScreenCentre()
5471 if (core->joystickEnabled)
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 << ")";
5482 if (game->cameraOffBounds)
5483 return getVectorToCursor();
5486 if (dsq->inputMode == INPUT_KEYBOARD)
5488 return getKeyDir() * 350;
5490 if (dsq->inputMode == INPUT_JOYSTICK)
5492 return (core->joystick.position * 350);
5494 return (core->mouse.position+offset) - Vector(400,300);
5498 Vector Avatar::getVectorToCursor(bool trueMouse)
5500 //return getVectorToCursorFromScreenCentre();
5501 Vector pos = dsq->getGameCursorPosition();
5506 if (dsq->inputMode == INPUT_KEYBOARD)
5508 pos = getKeyDir() * 350 + (position+offset);
5510 if (dsq->inputMode == INPUT_JOYSTICK)
5512 pos = core->joystick.position * 350 + (position+offset);
5516 return pos - (position+offset);
5517 //return core->mouse.position - Vector(400,300);
5520 void Avatar::startWallCrawl()
5522 lastWallNormal = wallNormal;
5523 state.crawlingOnWall = true;
5524 skeletalSprite.transitionAnimate("crawl", 0.1, -1);
5527 void Avatar::stopWallCrawl()
5529 state.crawlingOnWall = false;
5530 state.lockedToWall = false;
5534 void Avatar::action(int id, int state)
5536 if (id == ACTION_PRIMARY) { if (state) lmbd(); else lmbu(); }
5537 if (id == ACTION_SECONDARY) { if (state) rmbd(); else rmbu(); }
5539 if (id == ACTION_PRIMARY && state)// !state
5541 if (isMiniMapCursorOkay())
5543 if (this->state.lockedToWall && !this->state.crawlingOnWall)
5545 Vector test = getVectorToCursor();
5546 if (test.isLength2DIn(minMouse))
5549 // previously didn't fall off wall with mouse.... why?
5552 if (dsq->inputMode == INPUT_JOYSTICK || dsq->inputMode == INPUT_KEYBOARD)
5557 else if (test.isLength2DIn(maxMouse))
5559 this->state.lockedToWall = false;
5565 //if (dsq->continuity.setFlag("lockedToWall", 1)
5567 //!boneLock.entity &&
5568 if (boneLock.entity)
5569 wallNormal = boneLock.wallNormal;
5574 float dott = wallNormal.dot2D(test);
5577 // normal is 90 degrees within/on the right side
5580 startWallBurst(true);
5584 startWallBurst(false);
5590 float dott = wallNormal.dot2D(test);
5596 startWallBurst(true);
5608 wallNormal = getWallNormal(TileVector(position));
5609 Vector left = wallNormal.getPerpendicularLeft();
5610 Vector right = wallNormal.getPerpendicularRight();
5611 position += left*0.1;
5623 else if (!action.empty() && action[0] == 's')
5628 std::istringstream is(action.substr(1, action.size()));
5632 if (count >= 0 && count <= 7)
5634 core->setMousePosition(songIcons[count]->position);
5639 else if (id >= ACTION_SONGSLOT1 && id < ACTION_SONGSLOTEND)
5643 int count = (id - ACTION_SONGSLOT1)+1;
5647 if (dsq->continuity.form == FORM_SPIRIT)
5658 if (dsq->continuity.form != FORM_NORMAL)
5666 if (dsq->continuity.form != FORM_ENERGY)
5668 dsq->continuity.castSong(SONG_ENERGYFORM);
5673 if (dsq->continuity.form != FORM_BEAST)
5675 dsq->continuity.castSong(SONG_BEASTFORM);
5680 if (dsq->continuity.form != FORM_NATURE)
5682 dsq->continuity.castSong(SONG_NATUREFORM);
5687 if (dsq->continuity.form != FORM_SUN)
5689 dsq->continuity.castSong(SONG_SUNFORM);
5694 if (dsq->continuity.form != FORM_FISH)
5696 dsq->continuity.castSong(SONG_FISHFORM);
5701 if (dsq->continuity.form != FORM_SPIRIT)
5703 dsq->continuity.castSong(SONG_SPIRITFORM);
5708 if (dsq->continuity.form != FORM_DUAL)
5710 dsq->continuity.castSong(SONG_DUALFORM);
5715 if (dsq->continuity.form == FORM_NORMAL)
5721 dsq->continuity.castSong(SONG_SHIELDAURA);
5727 dsq->continuity.castSong(SONG_BIND);
5740 quickSongCastDelay = QUICK_SONG_CAST_DELAY;
5746 void Avatar::doRangePull(float dt)
5751 Vector dest = position + vel*dt;
5753 std::vector<Vector> vectors;
5754 for (int x = t.x-range; x <= t.x+range; x++)
5756 for (int y = t.y-range; y <= t.y+range; y++)
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))
5766 vectors.push_back(tile.worldVector());
5768 Vector obs = tile.worldVector();
5769 Vector mov = position - obs;
5771 int len = range*TILE_SIZE - mov.getLength2D();
5772 if (len < 0) len = 1;
5780 unsigned int amount = 5;
5781 if (amount > vectors.size())
5782 amount = vectors.size();
5783 std::vector<int>smallestDists;
5784 smallestDists.resize(amount);
5786 for (i = 0; i < smallestDists.size(); i++)
5788 smallestDists[i] = -1;
5790 std::vector<Vector> closestVectors;
5791 closestVectors.resize(amount);
5792 for (i = 0; i < vectors.size(); i++)
5794 Vector diff = dest - vectors[i];
5795 int dist = diff.getSquaredLength2D();
5796 for (int j = 0; j < smallestDists.size(); j++)
5798 if (dist < smallestDists[j] || smallestDists[j] == -1)
5800 for (int k = smallestDists.size()-1; k > j; k--)
5802 smallestDists[k] = smallestDists[k-1];
5803 closestVectors[k] = closestVectors[k-1];
5805 smallestDists[j] = dist;
5806 closestVectors[j] = vectors[i];
5811 for (i = 0; i < closestVectors.size(); i++)
5813 Vector obs = closestVectors[i];
5814 if (obs.x == 0 && obs.y == 0) continue;
5815 Vector mov = obs - position;
5817 int len = range*TILE_SIZE - mov.getLength2D();
5818 if (len < 0) len = 0;
5821 mov.setLength2D(len);
5826 if (total.x != 0 || total.y != 0)
5828 float vlen = vel.getLength2D();
5829 //float len = (range*TILE_SIZE - avgDist)/range*TILE_SIZE;
5832 if (bursting && swimming)
5834 total.setLength2D(dt*4000);
5838 //vel = Vector(0,0,0);
5840 total.setLength2D(dt*1000);
5844 total.setLength2D(dt*200);
5864 void Avatar::doRangePush(float dt)
5868 if (vel.getSquaredLength2D() < sqr(1)) return;
5871 TileVector t(position);
5872 std::vector<Vector> vectors;
5873 for (int x = t.x-range; x <= t.x+range; x++)
5875 for (int y = t.y-range; y <= t.y+range; y++)
5877 TileVector tile(x,y);
5878 if (dsq->game->isObstructed(tile))
5880 vectors.push_back(tile.worldVector());
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++)
5892 smallestDists[i] = -1;
5894 std::vector<Vector> closestVectors;
5895 closestVectors.resize(amount);
5896 for (int i = 0; i < vectors.size(); i++)
5898 Vector diff = position - vectors[i];
5899 int dist = diff.getSquaredLength2D();
5900 for (int j = 0; j < smallestDists.size(); j++)
5902 if (dist < smallestDists[j] || smallestDists[j] == -1)
5904 for (int k = smallestDists.size()-1; k > j; k--)
5906 smallestDists[k] = smallestDists[k-1];
5907 closestVectors[k] = closestVectors[k-1];
5909 smallestDists[j] = dist;
5910 closestVectors[j] = vectors[i];
5917 for (int i = 0; i < smallestDists.size(); i++)
5919 if (smallestDists[i] != -1)
5921 tot += smallestDists[i];
5925 float avgDist = range*TILE_SIZE;
5933 for (int i = 0; i < closestVectors.size(); i++)
5935 Vector obs = closestVectors[i];
5936 if (obs.x == 0 && obs.y == 0) continue;
5937 Vector mov = position - obs;
5939 int len = range*range*TILE_SIZE - mov.getLength2D();
5940 if (len < 0) len = 0;
5948 if (total.x != 0 || total.y != 0)
5950 float vlen = vel.getLength2D();
5951 //float len = (range*TILE_SIZE - avgDist)/range*TILE_SIZE;
5968 float d = total.dot2D(n);
5980 void Avatar::render()
5983 if (dsq->continuity.form == FORM_SPIRIT && !skeletalSprite.getParent())
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);
5995 #ifdef BBGE_BUILD_OPENGL
5997 glColor4f(1.0f,0.0f,0.0f,1.0f);
5999 glVertex3f(position.x, position.y, 0);
6000 glVertex3f(ropePos.x, ropePos.y, 0);
6007 if (activeAura == AURA_SHIELD)
6010 glColor4f(0,0.5,1,1);
6011 glTranslatef(shieldPosition.x, shieldPosition.y, 0);
6012 drawCircle(AURA_SHIELD_RADIUS, 8);
6019 void Avatar::onRender()
6025 if (RenderObject::renderPaths)
6030 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6031 glColor4f(1, 0, 0, 0.5);
6032 glTranslatef(400, 300, 0);
6038 //dsq->print(20, 600-100, "Naija: Hello there. My name is fred.");
6040 std::ostringstream os;
6041 os << lastPush.x << ", " << lastPush.y;
6047 glTranslatef(position.x, position.y, position.z);
6048 //glRotatef(0, 0, 1, -rotation.z);
6049 glDisable(GL_BLEND);
6051 glDisable(GL_LIGHTING);
6054 //glColor3f(1, 0, 0);
6056 //glColor3f(1, 0, 0);
6057 glVertex3f(lastPush.x*50, lastPush.y*50, 0);
6063 int Avatar::getBeamWidth()
6065 const int MAX_BEAM_LEN = 50;
6066 Vector mov = dsq->getGameCursorPosition() - this->position;
6068 TileVector t(position);
6069 Vector tile(t.x, t.y);
6071 while (c < MAX_BEAM_LEN)
6078 if (dsq->game->isObstructed(t))
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)
6094 // HACK: replace damage function
6095 //e->damage(1, 0, this);
6104 return c * TILE_SIZE;
6107 void Avatar::onGetEXP(unsigned int exp)
6109 dsq->continuity.exp += exp;
6112 void Avatar::onEnterState(int action)
6114 Entity::onEnterState(action);
6115 if (action == STATE_TRANSFORM)
6121 else if (action == STATE_PUSH)
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);
6129 else if (action == STATE_EATING)
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
6138 vel=vel2=Vector(0,0);
6139 skeletalSprite.animate("eat", 0, ANIM_OVERRIDE);
6146 void Avatar::onExitState(int action)
6148 Entity::onExitState(action);
6149 if (action == STATE_TRANSFORM)
6151 setState(STATE_IDLE);
6153 else if (action == STATE_PUSH)
6155 skeletalSprite.transitionAnimate("spin", 0.1);
6157 rotation.z = rotation.z+360;
6158 rotation.interpolateTo(Vector(0,0,rotation.z-360-90), 0.5);
6163 void Avatar::splash(bool down)
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();
6179 sound("splash-outof");
6181 dsq->postProcessingFx.enable(FXT_RADIALBLUR);
6182 dsq->postProcessingFx.radialBlurColor = Vector(1,1,1);
6183 dsq->postProcessingFx.intensity = 0.1;
6186 // make a splash effect @ current position
6191 if (waterBubble || lastJumpOutFromWaterBubble)
6193 lastJumpOutFromWaterBubble = false;
6197 Vector diff = position - waterBubble->nodes[0].position;
6198 a = MathFunctions::getAngleToVector(diff, 180);
6200 else if (lastWaterBubble)
6202 Vector diff = position - lastWaterBubble->nodes[0].position;
6203 a = MathFunctions::getAngleToVector(diff, 0);
6206 a = MathFunctions::getAngleToVector(vel+vel2, 0);
6209 dsq->spawnParticleEffect("Splash", position, a);
6212 //Vector(position.x, dsq->game->waterLevel.x));
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;
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);
6226 splash->setDecayRate(0.9);
6227 core->getTopStateData()->addRenderObject(splash, LR_PARTICLES);
6231 void Avatar::clampVelocity()
6234 std::ostringstream os;
6235 os << "currentMaxSpeed: " << currentMaxSpeed;
6238 float useSpeedMult = dsq->continuity.speedMult;
6239 bool inCurrent = isInCurrent();
6240 bool withCurrent = false;
6244 // if vel2 and vel are pointing the same way
6245 if (vel2.dot2D(vel) > 0)
6255 if (dsq->continuity.form == FORM_BEAST)
6257 currentMaxSpeed *= MULT_MAXSPEED_BEASTFORM;
6260 if (!inCurrent || (inCurrent && withCurrent))
6262 if (dsq->continuity.form == FORM_FISH)
6264 currentMaxSpeed *= MULT_MAXSPEED_FISHFORM;
6272 if (currentState == STATE_PUSH)
6274 currentMaxSpeed = pushMaxSpeed;
6278 setMaxSpeed(currentMaxSpeed * useSpeedMult);
6279 float cheatLen = vel.getSquaredLength2D();
6280 vel.capLength2D(getMaxSpeed());
6282 if (cheatLen > sqr(getMaxSpeed()))
6283 vel.setLength2D(getMaxSpeed());
6287 void Avatar::activateAura(AuraType aura)
6291 if (aura == AURA_SHIELD)
6293 shieldPoints = maxShieldPoints;
6294 auraEmitter.load("AuraShield");
6295 auraEmitter.start();
6296 if (dsq->loops.shield == BBGE_AUDIO_NOCHANNEL)
6299 play.name = "Shield-Loop";
6303 dsq->loops.shield = core->sound->playSfx(play);
6308 void Avatar::updateAura(float dt)
6310 if (auraTimer > 0 && dsq->continuity.form != FORM_SPIRIT)
6316 //shieldPosition = position + Vector(cos(auraTimer*4)*100, sin(auraTimer*4)*100);
6317 shieldPosition = position;
6319 float a = ((rotation.z)*PI)/180.0 + PI*0.5;
6320 shieldPosition = position + Vector(cosf(a)*100, sinf(a)*100);
6322 for (Shot::Shots::iterator i = Shot::shots.begin(); i != Shot::shots.end(); ++i)
6324 //&& (*i)->life > 0.2
6325 if ((*i) && dsq->game->isDamageTypeEnemy((*i)->getDamageType()) && (*i)->firer != this
6326 && (!(*i)->shotData || !(*i)->shotData->ignoreShield))
6329 Vector diff = (*i)->position - shieldPosition;
6330 if (diff.getSquaredLength2D() < sqr(AURA_SHIELD_RADIUS))
6332 shieldPoints -= (*i)->getDamage();
6333 auraHitEmitter.start();
6334 dsq->spawnParticleEffect("ReflectShot", (*i)->position);
6335 core->sound->playSfx("Shield-Hit");
6336 (*i)->position += diff;
6338 diff.setLength2D((*i)->maxSpeed);
6339 (*i)->velocity = diff;
6340 (*i)->reflectFromEntity(this);
6349 if (auraTimer < 5 || shieldPoints < 2)
6351 if (auraEmitter.name == "AURASHIELD")
6354 auraEmitter.load("AuraShieldLow");
6355 auraEmitter.start();
6359 if (auraTimer < 0 || shieldPoints < 0)
6366 void Avatar::stopAura()
6369 activeAura = AURA_NONE;
6371 if (dsq->loops.shield != BBGE_AUDIO_NOCHANNEL)
6373 core->sound->fadeSfx(dsq->loops.shield, SFT_OUT, 1);
6374 dsq->loops.shield = BBGE_AUDIO_NOCHANNEL;
6378 void Avatar::setHeadTexture(const std::string &name, float time)
6380 if (!bone_head) return;
6381 if (dsq->continuity.form == FORM_NORMAL /*&& dsq->continuity.costume.empty()*/)
6383 if (!name.empty() && (nocasecmp(lastHeadTexture, "singing")==0)) return;
6385 lastHeadTexture = name;
6386 stringToUpper(lastHeadTexture);
6387 std::string t = "Naija/";
6389 if (!dsq->continuity.costume.empty())
6391 if (nocasecmp(dsq->continuity.costume, "end")==0)
6394 t += dsq->continuity.costume;
6396 else if (dsq->continuity.form == FORM_BEAST)
6409 bone_head->setTexture(t);
6411 headTextureTimer = time;
6415 void Avatar::chargeVisualEffect(const std::string &tex)
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);
6437 void Avatar::updateFormVisualEffects(float dt)
6439 switch (dsq->continuity.form)
6443 Vector hairDir(96, -96);
6446 hairDir.x = -hairDir.x;
6452 lightFormGlow->position = this->position;
6453 lightFormGlowCone->position = this->position;
6456 MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), getVectorToCursorFromScreenCentre(), angle);
6457 angle = 180-(360-angle);
6459 //lightFormGlowCone->rotation.interpolateTo(Vector(0,0,angle), 0.1);
6460 lightFormGlowCone->rotation = Vector(0,0,angle);
6462 static float lfgTimer = 0;
6466 //debugLog("lightFormGlow to front");
6467 lightFormGlow->moveToFront();
6468 lightFormGlowCone->moveToFront();
6472 if (this->isInDarkness())
6474 lightFormGlowCone->alphaMod = 1;
6475 lightFormGlow->alphaMod = 1;
6479 lightFormGlow->alphaMod = 0;
6480 lightFormGlowCone->alphaMod = 0;
6485 skeletalSprite.update(dt);
6486 skeletalSprite.position = bodyPosition;
6491 void Avatar::stopBurst()
6494 //burstDelay = BURST_DELAY;
6497 animatedBurst = false;
6501 biteLeftEmitter.stop();
6502 biteRightEmitter.stop();
6503 // lastWallJumpPos = Vector(0,0,0);
6506 int Avatar::getCursorQuadrant()
6508 //Vector diff = getVectorToCursorFromScreenCentre();
6509 Vector diff = getVectorToCursor();
6510 if (diff.isLength2DIn(40))
6515 //diff = getForward();
6516 float angle = atanf(diff.y / diff.x);
6517 //float angle = rotation.z;
6520 std::ostringstream os;
6521 os << "angle: " << angle;
6527 if (angle > -1.6 && angle <= 0 && diff.x > 0)
6529 else if (angle > -1.6 && angle <= 0 && diff.x < 0 && diff.y > 0)
6531 else if (angle < 1.6 && angle >= 0 && diff.y < 0)
6539 int Avatar::getQuadrantDirection(int lastQuad, int quad)
6541 int diff = quad - lastQuad;
6542 //if (lastQuad==0) return 0;
6543 if ((lastQuad==4 && quad == 1))
6547 if (lastQuad==1 && quad==4)
6556 void Avatar::startRoll(int dir)
6558 if (!rolling && !state.backFlip)
6560 if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
6562 rollRightEmitter.load("EnergyRollRight");
6563 rollLeftEmitter.load("EnergyRollLeft");
6567 rollRightEmitter.load("RollRight");
6568 rollLeftEmitter.load("RollLeft");
6572 Animation *a = skeletalSprite.getCurrentAnimation();
6573 //debugLog("start roll!");
6574 if (!a || a->name != getRollAnimName())
6576 skeletalSprite.transitionAnimate(getRollAnimName(), 0.2, -1);
6579 rollRightEmitter.load("RollRight");
6580 rollLeftEmitter.load("RollLeft");
6582 rollRightEmitter.start();
6583 rollLeftEmitter.start();
6585 rollRightEmitter.stop();
6586 rollLeftEmitter.stop();
6591 rollRightEmitter.start();
6594 rollLeftEmitter.start();
6597 //dsq->playVisualEffect(VFX_RIPPLE, Vector());
6600 if (dsq->loops.roll == BBGE_AUDIO_NOCHANNEL && _isUnderWater)
6603 play.name = "RollLoop";
6608 dsq->loops.roll = core->sound->playSfx(play);
6610 else if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL && !_isUnderWater)
6612 core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 0.5);
6615 //HACK: make this dt based
6616 static int rollBits = 0;
6617 rollBits = rollBits + 1;
6621 core->sound->playSfx("Roll2");
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));
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));
6638 void Avatar::stopRoll()
6641 lastQuadDir = lastQuad = 0;
6644 rollLeftEmitter.stop();
6645 rollRightEmitter.stop();
6646 state.rollTimer = 0;
6648 if (dsq->loops.roll != BBGE_AUDIO_NOCHANNEL)
6650 core->sound->fadeSfx(dsq->loops.roll, SFT_OUT, 1);
6651 dsq->loops.roll = BBGE_AUDIO_NOCHANNEL;
6656 void Avatar::stopWallJump()
6660 lastWallJumpPos = Vector(0,0,0);
6663 void Avatar::updateWallJump(float dt)
6665 if (wallBurstTimer > 0)
6667 wallBurstTimer -= dt;
6668 if (wallBurstTimer < 0)
6670 // wall jump failed!
6676 void Avatar::updateRoll(float dt)
6678 if (!inputEnabled || dsq->continuity.getWorldType() == WT_SPIRIT)
6684 if (state.lockedToWall || isSinging()) return;
6689 std::ostringstream os;
6690 os << "rollDelay: " << rollDelay;
6696 // stop the animation
6701 if (!_isUnderWater && isActing(ACTION_ROLL))
6706 if (!core->mouse.buttons.left && dsq->inputMode == INPUT_MOUSE && !isActing(ACTION_ROLL))
6716 for (int i = 0; i < dsq->entities.size(); i++)
6718 Entity *e = dsq->entities[i];
6719 if (e->getEntityType() == ET_ENEMY && (e->position - this->position).isLength2DIn(350))
6721 //e->move(dt, 500, 1, this);
6722 Vector diff = (position - e->position);
6723 diff.setLength2D(1000*dt);
6728 if (dsq->continuity.form == FORM_ENERGY && dsq->continuity.hasFormUpgrade(FORMUPGRADE_ENERGY1))
6733 if (e->getEntityType() == ET_ENEMY && (e->position - this->position).isLength2DIn(256) && e->isDamageTarget(DT_AVATAR_ENERGYROLL))
6737 d.damageType = DT_AVATAR_ENERGYROLL;
6744 state.rollTimer += dt;
6745 if (state.rollTimer > 0.55)
6747 state.rollTimer = 0;
6748 if (dsq->continuity.form == FORM_DUAL)
6750 switchDualFormMode();
6751 state.rollTimer = -1;
6756 //HACK: -ish, fixes low frame rate roll stuck problem?
6763 // NOTE: does this fix the roll problem?
6768 if (isActing(ACTION_ROLL))
6772 if (rollDelay < 0.5)
6774 startRoll(isfh()?1:-1);
6776 float amt = dt * 1000;
6785 rotation.capRotZ360();
6792 int quad = getCursorQuadrant();
6793 if (lastQuad != quad)
6798 quadDir = getQuadrantDirection(lastQuad, quad);
6799 if (quadDir != 0 && lastQuadDir == quadDir && rollDelay > 0)
6809 else if (rollDidOne == 2)
6817 std::ostringstream os;
6818 os << "quad: " << quad << " lastQuad: " << lastQuad << " lastQuadDir: " << lastQuadDir;
6826 lastQuadDir = quadDir;
6830 lastQuadDir = quadDir;
6839 int Avatar::getStopDistance()
6841 return STOP_DISTANCE;
6844 int Avatar::getBurstDistance()
6846 return BURST_DISTANCE;
6849 void Avatar::updateTummy(float dt)
6851 if (dsq->continuity.form == FORM_BEAST)
6853 //dsq->shakeCamera(5, 0.1);
6859 if (tummyTimer > TUMMY_TIME)
6861 //core->sound->playSfx("Digest");
6871 void Avatar::setWasUnderWater()
6873 state.wasUnderWater = isUnderWater();
6876 bool Avatar::canActivateStuff()
6878 return dsq->continuity.form != FORM_SPIRIT;
6881 bool Avatar::canQuickSong()
6883 return !isSinging() && !isEntityDead() && isInputEnabled() && quickSongCastDelay <= 0;
6886 void Avatar::updateJoystick(float dt)
6891 if (core->joystick.dpadUp)
6893 if (dsq->continuity.hasSong(SONG_ENERGYFORM) && dsq->continuity.form != FORM_ENERGY)
6895 quickSongCastDelay = QUICK_SONG_CAST_DELAY;
6896 dsq->continuity.castSong(SONG_ENERGYFORM);
6899 else if (core->joystick.dpadDown && dsq->continuity.hasSong(SONG_BEASTFORM) && dsq->continuity.form != FORM_BEAST)
6901 quickSongCastDelay = QUICK_SONG_CAST_DELAY;
6902 dsq->continuity.castSong(SONG_BEASTFORM);
6904 else if (core->joystick.dpadLeft && dsq->continuity.hasSong(SONG_SUNFORM) && dsq->continuity.form != FORM_SUN)
6906 quickSongCastDelay = QUICK_SONG_CAST_DELAY;
6907 dsq->continuity.castSong(SONG_SUNFORM);
6909 else if (core->joystick.dpadRight && dsq->continuity.hasSong(SONG_NATUREFORM) && dsq->continuity.form != FORM_NATURE)
6911 quickSongCastDelay = QUICK_SONG_CAST_DELAY;
6912 dsq->continuity.castSong(SONG_NATUREFORM);
6918 void Avatar::applyRidingPosition()
6922 position = riding->getRidingPosition();
6923 lastPosition = position;
6924 rotation.z = riding->getRidingRotation();
6926 if (riding->getRidingFlip())
6936 //state.wasUnderWater = _isUnderWater;
6940 void Avatar::adjustHeadRot()
6945 if (bone_head->rotation.z > 0)
6947 bone_head->internalOffset.x = (bone_head->rotation.z/30.0)*5;
6948 //bone_head->internalOffset.y = (bone_head->rotation.z/30.0)*1;
6951 if (bone_head->rotation.z < 0)
6953 bone_head->internalOffset.x = (bone_head->rotation.z/(-10.0))*-4;
6954 bone_head->internalOffset.y = (bone_head->rotation.z/(-10.0))*-2;
6959 void Avatar::endOfGameState()
6961 state.lookAtEntity = 0;
6962 setInvincible(true);
6965 bool didRotationFix = true;
6967 void timerEffectStart(Timer *timer, ParticleEffect *effect)
6969 if (timer->isActive() && !effect->isRunning())
6973 else if (!timer->isActive() && effect->isRunning())
6979 void Avatar::updateFoodParticleEffects()
6981 timerEffectStart(&dsq->continuity.speedMultTimer, &speedEmitter);
6982 timerEffectStart(&dsq->continuity.defenseMultTimer, &defenseEmitter);
6983 timerEffectStart(&dsq->continuity.invincibleTimer, &invincibleEmitter);
6984 timerEffectStart(&dsq->continuity.regenTimer, ®enEmitter);
6987 void Avatar::updateLookAt(float dt)
6989 //if (dsq->overlay->alpha != 0) return;
6990 if (dsq->game->isShuttingDownGameState()) return;
6991 if (headTextureTimer > 0)
6993 headTextureTimer -= dt;
6994 if (headTextureTimer <= 0)
6996 headTextureTimer = 0;
7001 if (dsq->continuity.form == FORM_FISH)
7003 Bone *b = skeletalSprite.getBoneByIdx(0);
7005 b->setAnimated(Bone::ANIM_ALL);
7009 const float blinkTime = 5.0;
7010 state.blinkTimer += dt;
7011 if (state.blinkTimer > blinkTime)
7013 if (lastHeadTexture.empty())
7015 //if (dsq->continuity.form == FORM_NORMAL)
7016 setHeadTexture("blink", 0.1);
7019 state.blinkTimer = blinkTime-0.2;
7023 state.blinkTimer = rand()%2;
7028 state.blinkTimer -= dt;
7034 const float lookAtTime = 0.8;
7035 if (core->mouse.buttons.middle && !state.lockedToWall && isInputEnabled())
7037 didRotationFix = false;
7038 bone_head->setAnimated(Bone::ANIM_POS);
7039 bone_head->lookAt(dsq->getGameCursorPosition(), lookAtTime, -10, 30, -90);
7044 if (state.lookAtEntity && (state.lookAtEntity->isEntityDead() || state.lookAtEntity->isDead() || state.lookAtEntity->isv(EV_LOOKAT,0) || swimming))
7046 state.lookAtEntity = 0;
7048 // find an object of interest
7049 if (isv(EV_LOOKAT, 1) && state.lookAtEntity && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->isAnimating() && !state.lockedToWall && !swimming)
7051 didRotationFix = false;
7052 bone_head->setAnimated(Bone::ANIM_POS);
7053 bone_head->lookAt(state.lookAtEntity->getLookAtPoint(), lookAtTime, -10, 30, -90);
7055 if (!((state.lookAtEntity->position - position).isLength2DIn(1000)))
7057 state.lookAtEntity = 0;
7059 state.updateLookAtTime += dt;
7064 bone_head->setAnimated(Bone::ANIM_ALL);
7066 if (!didRotationFix && !bone_head->rotationOffset.isInterpolating())
7069 didRotationFix = true;
7070 float oldRot = bone_head->rotation.z;
7072 skeletalSprite.updateBones();
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);
7078 std::ostringstream os;
7079 os << "rotationOffset lerp " << bone_head->rotationOffset.z;
7085 bone_head->rotationOffset = bone_head->rotation.z;
7086 bone_head->rotation.z = 0;
7087 bone_head->rotationOffset.interpolateTo(0, 0.2);
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);
7095 if (state.updateLookAtTime > 1.5)
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))
7101 std::ostringstream os;
7102 os << "Nearest: " << state.lookAtEntity->name;
7106 state.updateLookAtTime = 0;
7107 //if (dsq->continuity.form == FORM_NORMAL)
7108 //setHeadTexture("blink", 0.1);
7111 if (state.lookAtEntity->getEntityType() == ET_NEUTRAL)
7113 //if (dsq->continuity.form == FORM_NORMAL)
7114 //setHeadTexture("smile", 1);
7118 if (!state.lookAtEntity->naijaReaction.empty())
7120 setHeadTexture(state.lookAtEntity->naijaReaction, 1.5);
7125 state.lookAtEntity = 0;
7127 std::ostringstream os;
7128 os << state.updateLookAtTime << " : found no entities";
7131 //state.updateLookAtTime -= 0.3;
7134 //skeletalSprite.animate("blink", 2, ANIMLAYER_HEADOVERRIDE);
7140 Vector Avatar::getHeadPosition()
7143 return bone_head->getWorldPosition();
7147 bool lastCursorKeyboard = false;
7149 bool Avatar::isMiniMapCursorOkay()
7151 //!(dsq->getMouseButtonState(0) || dsq->getMouseButtonState(1))
7152 return ((dsq->inputMode != INPUT_MOUSE) || (!dsq->game->miniMapRender || !dsq->game->miniMapRender->isCursorIn()));
7155 void Avatar::updateCursorFromKeyboard()
7158 // why return when singing??
7159 //if (isSinging()) return;
7160 if (!isInputEnabled()) return;
7164 if (isActing(ACTION_SINGLEFT))
7166 if (isActing(ACTION_SINGRIGHT))
7168 if (isActing(ACTION_SINGUP))
7170 if (isActing(ACTION_SINGDOWN))
7174 diff.setLength2D(dist);
7175 core->mouse.position = Vector(400,300) + diff;
7176 lastCursorKeyboard = true;
7178 else if (lastCursorKeyboard)
7180 debugLog("HEY!: lastCursorKeyboard mouse position reset");
7181 core->mouse.position = Vector(400,300);
7182 lastCursorKeyboard = false;
7183 dsq->toggleCursor(false, 0.2);
7187 if (isInputEnabled() && (!core->mouse.change.isZero()))
7189 dsq->toggleCursor(true, 0.2);
7194 void Avatar::onUpdate(float dt)
7196 BBGE_PROF(Avatar_onUpdate);
7198 // animation debug code
7200 for (int i = 0; i < 8; i++)
7202 if (skeletalSprite.getAnimationLayer(i))
7204 if (skeletalSprite.getAnimationLayer(i)->isAnimating())
7206 std::ostringstream os;
7207 os << "anim layer: " << i << " - " << skeletalSprite.getAnimationLayer(i)->getCurrentAnimation()->name;
7209 //debugLog("anim layer 0: " + skeletalSprite.getAnimationLayer(0)->getCurrentAnimation()->name);
7217 #ifdef AQ_TEST_QUADTRAIL
7218 quadTrail->addPoint(position);
7223 if (dsq->continuity.light)
7225 lightFormGlow->scale = Vector(6,6) + Vector(4,4)*dsq->continuity.light;
7229 lightFormGlow->scale = Vector(6,6);
7233 applyRidingPosition();
7236 activateEntity->activate();
7240 headPosition = bone_head->getWorldPosition();
7245 debugLog("detected velocity NaN");
7249 if (canWarpDelay > 0)
7251 canWarpDelay = canWarpDelay - dt;
7252 if (canWarpDelay < 0)
7268 if (doubleClickDelay > 0)
7270 doubleClickDelay = doubleClickDelay - dt;
7271 if (doubleClickDelay < 0) doubleClickDelay = 0;
7275 if (isInputEnabled())
7279 if (!webBitTimer.isActive())
7281 webBitTimer.start(0.5);
7283 web->setPoint(curWebPoint, position);
7285 if (webBitTimer.updateCheck(dt))
7287 webBitTimer.start(0.5);
7289 curWebPoint = web->addPoint(position);
7293 if (!dsq->game->isPaused() && isActing(ACTION_LOOK) && !dsq->game->avatar->isSinging() && dsq->game->avatar->isInputEnabled() && !dsq->game->isInGameMenu())
7310 if (core->afterEffectManager)
7316 core->afterEffectManager->setActiveShader(AS_WASHOUT);
7317 //core->afterEffectManager->setActiveShader(AS_NONE);
7321 if (dsq->user.video.shader != AS_NONE)
7323 core->afterEffectManager->setActiveShader((ActiveShader)dsq->user.video.shader);
7327 if (damageTimer.isActive() && dsq->isShakingCamera())
7329 if (dsq->user.video.blur)
7330 core->afterEffectManager->setActiveShader(AS_BLUR);
7334 core->afterEffectManager->setActiveShader(AS_NONE);
7341 if (!targets.empty())
7343 if (targets[0] && (targets[0]->position - this->position).getSquaredLength2D() > sqr(TARGET_RANGE))
7349 //spawnChildClone(4);
7350 if (!core->cameraRot.isInterpolating())
7352 core->cameraRot.interpolateTo(Vector(0,0,360), 30, -1);
7354 for (int i = 0; i < targets.size(); i++)
7356 if (targets[i] && !this->isEntityDead())
7358 targetQuads[i]->alpha.interpolateTo(1,0.1);
7359 targetQuads[i]->position = targets[i]->position;
7363 if (targetQuads[i]->alpha.getValue()>0)
7364 targetQuads[i]->alpha.interpolateTo(0,0.1);
7369 Entity::onUpdate(dt);
7371 if (isEntityDead() && skeletalSprite.getCurrentAnimation()->name != "dead")
7374 biteLeftEmitter.stop();
7375 biteRightEmitter.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");
7389 dsq->game->toggleOverrideZoom(false);
7392 if (dsq->user.control.targeting)
7393 updateTargets(dt, false);
7397 updateTargetQuads(dt);
7399 updateDualFormGlow(dt);
7402 updateFoodParticleEffects();
7404 if (!dsq->game->isPaused())
7407 _isUnderWater = isUnderWater();
7410 if (!_isUnderWater && state.wasUnderWater)
7413 // "falling" out, not bursting out
7414 int fallOutSpeed = 200;
7419 bool waterBubbleRect = (waterBubble && waterBubble->pathShape == PATHSHAPE_RECT);
7421 //&& !waterBubbleRect
7422 if (!riding && (!bursting && vel.isLength2DIn(fallOutSpeed)))
7427 // prevent from falling out
7429 waterBubble->clampPosition(&position);
7435 if (!dsq->game->waterLevel.isInterpolating())
7439 position.y = dsq->game->waterLevel.x + collideRadius;
7446 lastJumpOutFromWaterBubble = true;
7448 lastJumpOutFromWaterBubble = false;
7450 lastWaterBubble = waterBubble;
7452 BBGE_PROF(Avatar_splashOut);
7455 if (dsq->continuity.form != FORM_FISH)
7457 vel *= vars->jumpVelocityMod; // 1.25;
7458 vel.capLength2D(2000);
7459 currentMaxSpeed *= 2.0;
7464 vel.capLength2D(1500);
7465 currentMaxSpeed *= 1.5;
7469 fallGravityTimer = 0.0;
7476 if (!dsq->mod.isActive() && dsq->continuity.getFlag("leftWater")==0 && dsq->game->sceneName.find("veil")!=std::string::npos)
7478 setInvincible(true);
7479 setv(EV_NOINPUTNOVEL, 0);
7485 dsq->continuity.setFlag("leftWater", 1);
7487 core->sound->fadeMusic(SFT_OUT, 2);
7489 dsq->game->avatar->disableInput();
7490 dsq->gameSpeed.interpolateTo(0.1, 0.5);
7492 //dsq->sound->setMusicFader(0.5, 0.5);
7493 core->sound->playSfx("NaijaGasp");
7498 dsq->voiceOnce("Naija_VeilCrossing");
7501 dsq->gameSpeed.interpolateTo(1, 0.2);
7503 dsq->sound->playMusic("Veil", SLT_LOOP, SFT_CROSS, 20);
7505 //dsq->sound->setMusicFader(1.0, 1);
7507 dsq->game->avatar->enableInput();
7509 setv(EV_NOINPUTNOVEL, 1);
7511 setInvincible(false);
7513 //dsq->continuity.setFlag("leftWater", 0);
7518 state.outOfWaterTimer = 0;
7519 state.outOfWaterVel = vel;
7523 if (currentMaxSpeed > dsq->v.maxOutOfWaterSpeed)
7525 currentMaxSpeed = dsq->v.maxOutOfWaterSpeed;
7527 if (currentMaxSpeed < 1200)
7529 currentMaxSpeed = 1200;
7533 else if (_isUnderWater && !state.wasUnderWater)
7538 lastOutOfWaterMaxSpeed = getMaxSpeed();
7539 lastOutOfWaterMaxSpeed *= 0.75;
7541 if (lastOutOfWaterMaxSpeed > 1000)
7542 lastOutOfWaterMaxSpeed = 1000;
7544 fallGravityTimer = 0.5;
7546 fallGravityTimer *= 1.5;
7556 //skeletalSprite.getBoneByIdx(3)->getWorldPosition();
7558 state.wasUnderWater = _isUnderWater;
7562 state.outOfWaterTimer += dt;
7563 if (state.outOfWaterTimer > 100)
7564 state.outOfWaterTimer = 100;
7568 if (!state.backFlip && !_isUnderWater && state.outOfWaterTimer < 0.1 && !riding && !boneLock.on)
7570 const int check = 64;
7571 Vector m = getVectorToCursor();
7572 if (state.outOfWaterVel.x < 0 && m.x > check)
7576 if (state.outOfWaterVel.x > 0 && m.x < -check)
7583 if (core->afterEffectManager && _isUnderWater)
7585 if (swimming && vel.getSquaredLength2D() > sqr(200))
7588 while (rippleTimer > RIPPLE_INTERVAL)
7591 core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),core->screenCenter,0.05,0.08,15,0.2f, 1.2));
7593 //core->afterEffectManager->addEffect(new ShockEffect(Vector(400,300),0.01,0.002f,15,0.1f));
7603 offset.y = sin(bobTimer)*5 - 2.5f;
7611 if (isEntityDead()) return;
7613 if (flourishTimer.updateCheck(dt))
7616 rotationOffset.z = 0;
7619 if (isInputEnabled())
7620 stillTimer.update(dt);
7622 if (vel.isZero()) //&& !isSinging())
7624 if (!stillTimer.isActive())
7626 stillTimer.startStopWatch();
7627 //debugLog("start stillTimer");
7635 flourishPowerTimer.updateCheck(dt);
7639 if (songInterfaceTimer < 1)
7640 songInterfaceTimer += dt;
7644 if (quickSongCastDelay>0)
7646 quickSongCastDelay -= dt;
7647 if (quickSongCastDelay < 0)
7648 quickSongCastDelay = 0;
7650 if (ripples && _isUnderWater)
7653 if (rippleDelay < 0)
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));
7661 if (dsq->continuity.tripTimer.isActive())
7663 static int tripCount = 0;
7674 EMOTE_NAIJAEVILLAUGH = 0
7675 EMOTE_NAIJAGIGGLE = 1
7676 EMOTE_NAIJALAUGH = 2
7677 EMOTE_NAIJASADSIGH = 3
7682 float p = dsq->continuity.tripTimer.getPerc();
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));
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));
7696 dsq->shakeCamera(2, 4);
7700 dsq->emote.playSfx(2);
7702 dsq->emote.playSfx(0);
7708 dsq->shakeCamera(10, 4);
7710 dsq->shakeCamera(5, 4);
7711 tripper->color.interpolateTo(Vector(1, 0.2, 0.2), 3);
7713 dsq->emote.playSfx(6);
7721 if (position.isInterpolating())
7723 lastPosition = position;
7727 updateCursorFromKeyboard();
7728 updateFormVisualEffects(dt);
7734 if (dsq->autoSingMenuOpen)
7736 if (micNote != -1 && !this->isSinging())
7738 openedFromMicInput = true;
7739 openSingingInterface();
7741 if (micNote == -1 && isSinging() && openedFromMicInput)
7743 openedFromMicInput = false;
7744 closeSingingInterface();
7748 if (formAbilityDelay > 0)
7750 formAbilityDelay -= dt;
7751 if (formAbilityDelay < 0)
7752 formAbilityDelay = 0;
7756 if (getState() == STATE_PUSH)
7761 if (rotation.z > 360)
7764 rotateToVec(vel, 0, -90);
7765 if (vel.x < 0&& !isfh())
7767 else if (vel.x > 0 && isfh())
7785 ropePos += ropeVel*dt;
7786 if (dsq->game->isObstructed(TileVector(ropePos)))
7791 std::ostringstream os;
7792 os << "ropePos (" << ropePos.x << ", " << ropePos.y << ")";
7806 Vector add = (ropePos - position);
7807 if (add.getSquaredLength2D() > sqr(200))
7809 add.setLength2D(4000);
7816 updateSingingInterface(dt);
7820 if (pullTarget->life < 1 || !pullTarget->isPullable())
7822 pullTarget->stopPull();
7832 EnergyTendril *t = new EnergyTendril(avatar->position, pullTarget->position);
7833 core->addRenderObject(t, LR_PARTICLES);
7843 if (formTimer > 2.0 && dsq->continuity.form == FORM_SPIRIT)
7845 changeForm(FORM_NORMAL, true);
7848 if (pickingPullTarget)
7850 //debugLog("picking pull target");
7851 Entity *closest = 0;
7852 int smallestDist = -1;
7857 if (e->isPullable() && e->life == 1)
7859 if (e->isCoordinateInside(dsq->getGameCursorPosition()))
7861 int dist = (e->position - dsq->getGameCursorPosition()).getSquaredLength2D();
7862 if (dist < smallestDist || smallestDist == -1)
7864 smallestDist = dist;
7870 potentialPullTarget = closest;
7873 if (dsq->continuity.form == FORM_SPIRIT)
7875 if (useSpiritDistance)
7879 if (!(bodyPosition - position).isLength2DIn(SPIRIT_RANGE))
7881 changeForm(FORM_NORMAL);
7888 changeForm(FORM_NORMAL);
7893 float revertGrace = 0.4;
7894 static bool revertButtonsAreDown = false;
7895 if (inputEnabled && (dsq->inputMode == INPUT_KEYBOARD || dsq->inputMode == INPUT_MOUSE) && (!pathToActivate && !entityToActivate))
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))
7903 if (!revertButtonsAreDown)
7905 revertTimer = revertGrace;
7906 revertButtonsAreDown = true;
7908 else if (revertButtonsAreDown)
7910 if (revertTimer > 0)
7913 if (revertTimer < 0)
7920 //&& !isActing(ACTION_PRIMARY) && !isActing(ACTION_SECONDARY)
7921 else if ((!core->mouse.pure_buttons.left && !core->mouse.pure_buttons.right))
7923 if (revertTimer > 0 && getVectorToCursor(true).isLength2DIn(minMouse) && state.spellCharge < revertGrace+0.1)
7926 //changeForm(FORM_NORMAL);
7928 revertButtonsAreDown = false;
7934 revertButtonsAreDown = false;
7938 if (this->state.crawlingOnWall)
7942 wallNormal = dsq->game->getWallNormal(position);
7943 if (wallNormal.dot2D(lastWallNormal)<=0.3)
7949 Vector left = wallNormal.getPerpendicularLeft();
7950 Vector right = wallNormal.getPerpendicularRight();
7953 Vector test = getVectorToCursor();
7954 if (!test.isLength2DIn(64))
7959 if (test.dot2D(left)>0)
7964 move.setLength2D(800);
7966 if (move.x > 0 && !isfh())
7968 if (move.x < 0 && isfh())
7971 position += move*dt;
7972 rotateToVec(wallNormal, 0.1);
7987 //if (core->getNestedMains() == 1)
7992 const float leachHurtInterval = 3;
7993 state.leachTimer += dt;
7994 if (state.leachTimer > leachHurtInterval)
7996 state.leachTimer -= leachHurtInterval;
7998 d.damage = int(leaches/3);
8000 //hit(0, 0, SPELL_NONE, int(leaches/3));
8006 if (getState() != STATE_TRANSFORM && dsq->continuity.getWorldType() == WT_NORMAL)
8007 //if (dsq->continuity.form == FORM_ENERGY)
8009 //if (dsq->continuity.selectedSpell == SPELL_SHOCK)
8011 formAbilityUpdate(dt);
8013 // is this really necessary??
8015 // this allows the player to start charging quickly after firing
8017 if (isActing("charge") && !charging && spellCastDelay == 0 && inputEnabled)
8022 // maybe not useful anymore
8024 if (!isActing("charge") && charging && spellChargeDelay == 0 && inputEnabled)
8030 if (state.useItemDelay.updateCheck(dt))
8034 ActionMapper::onUpdate(dt);
8040 if (state.blindTimer.updateCheck(dt))
8042 state.blind = false;
8043 removeBlindEffects();
8047 /*&& this->getSelectedSpell() == SPELL_ENERGYBLAST*/
8049 // HACK: hacked out for now
8052 if (charging && !targets.empty() && targets[0] == 0 && state.spellCharge > 0.2
8053 && dsq->continuity.form == FORM_ENERGY)
8055 for (int i = 0; i < dsq->entities.size(); i++)
8057 Entity *e = dsq->entities[i];
8058 if (e && e != this && e->isAvatarAttackTarget() && !e->isEntityDead() && e->isAffectedBySpell(SPELL_ENERGYBLAST))
8060 ScriptedEntity *se = (ScriptedEntity*)e;
8061 if ((e->position - dsq->getGameCursorPosition()).getSquaredLength2D() < sqr(64))
8072 if (boneLock.entity != 0)
8075 std::ostringstream os;
8076 os << "boneLock.wallNormal(" << boneLock.wallNormal.x << ", " << boneLock.wallNormal.y << ")";
8079 if (!_isUnderWater && !(boneLock.wallNormal.y < -0.03))
8081 if (lockToWallFallTimer == -1)
8082 lockToWallFallTimer = 0.4;
8085 lockToWallFallTimer = -1;
8088 if (lockToWallFallTimer > 0)
8090 lockToWallFallTimer -= dt;
8091 if (lockToWallFallTimer <= 0)
8097 if (spellCastDelay > 0)
8099 spellCastDelay -= dt;
8100 if (spellCastDelay <= 0)
8106 if (state.lockToWallDelay.updateCheck(dt))
8110 if (state.tapTimer.updateCheck(dt))
8114 if (pushingOffWallEffect > 0)
8116 pushingOffWallEffect -= dt;
8117 if (pushingOffWallEffect <= 0)
8119 lastLockToWallPos = Vector(0,0);
8120 pushingOffWallEffect = 0;
8121 if (vel.getSquaredLength2D() > sqr(1200))
8123 vel.setLength2D(1200);
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;
8136 MathFunctions::calculateAngleBetweenVectorsInDegrees(this->position, dsq->getGameCursorPosition(), angle);
8137 beam->rotation.z = angle+90;
8138 beam->position.z = 3;
8139 //collideBeamWithEntities();
8142 if (state.dodgeEffectTimer.updateCheck(dt))
8144 vel.capLength2D(vars->maxSwimSpeed);
8146 if (vel.getSquaredLength2D() > sqr(vars->maxSwimSpeed))
8147 vel.setLength2D(vars->maxSwimSpeed);
8151 if (dodgeEffectTimer > 0)
8153 dodgeEffectTimer -= dt;
8154 if (dodgeEffectTimer <= 0)
8156 dodgeEffectTimer = 0;
8157 if (vel.getSquaredLength2D() > sqr(vars->maxSwimSpeed))
8158 vel |= vars->maxSwimSpeed;
8165 if (dodgeDelay <= 0)
8173 text->position = position + Vector(100);
8179 chargeGraphic->position = this->position;
8180 chargeGraphic->position.z = position.z + 0.05;
8182 state.spellCharge += dt;
8183 switch (dsq->continuity.form)
8187 if (state.spellCharge > 1.5 && chargeLevelAttained <1)
8189 chargeLevelAttained = 1.5;
8190 core->sound->playSfx("PowerUp");
8191 chargingEmitter->load("ChargingEnergy2");
8197 if (state.spellCharge >= 1.4 && chargeLevelAttained<1)
8199 chargeLevelAttained = 1;
8201 core->sound->playSfx("PowerUp");
8202 //debugLog("charge visual effect 2");
8203 chargeEmitter->load("ChargeDualForm");
8204 chargeEmitter->start();
8206 chargingEmitter->load("ChargedDualForm");
8207 chargingEmitter->start();
8211 //chargeVisualEffect("particles/energy-charge-2");
8214 if (state.spellCharge >= 1.5 && chargeLevelAttained<2)
8216 chargeLevelAttained = 2;
8218 core->sound->playSfx("PowerUp");
8219 //debugLog("charge visual effect 2");
8220 chargeEmitter->load("EnergyCharge");
8221 chargeEmitter->start();
8223 //chargeVisualEffect("particles/energy-charge-2");
8231 if (state.spellCharge >= 0.99 && chargeLevelAttained<1)
8233 chargeLevelAttained = 1;
8234 debugLog("charge visual effect 1");
8235 chargeVisualEffect("energy-charge-1");
8239 if (state.spellCharge >= 1.5 && chargeLevelAttained<2)
8241 chargeLevelAttained = 2;
8242 core->sound->playSfx("PowerUp");
8243 //debugLog("charge visual effect 2");
8244 chargeEmitter->load("ChargeEnergy");
8245 chargeEmitter->start();
8248 chargingEmitter->load("ChargingEnergy2");
8249 //chargeVisualEffect("particles/energy-charge-2");
8255 if (state.spellCharge >= 0.9 && chargeLevelAttained<2)
8257 chargeLevelAttained = 2;
8258 core->sound->playSfx("PowerUp");
8259 chargeEmitter->load("ChargeNature2");
8260 chargeEmitter->start();
8262 chargingEmitter->load("ChargingNature2");
8263 chargingEmitter->start();
8266 if (state.spellCharge >= 0.5 && chargeLevelAttained<1)
8268 chargeLevelAttained = 1;
8269 core->sound->playSfx("PowerUp");
8270 chargeEmitter->load("ChargeNature");
8271 chargeEmitter->start();
8274 if (state.spellCharge >= 2.0 && chargeLevelAttained<2)
8276 chargeLevelAttained = 2;
8277 core->sound->playSfx("PowerUp");
8278 chargeEmitter->load("ChargeNature2");
8279 chargeEmitter->start();
8281 chargingEmitter->load("ChargingNature2");
8282 chargingEmitter->start();
8290 float angle = 3.14f - ((rotation.z/180)*3.14f);
8293 //hair->hairNodes[0].position = position + Vector(sin(angle)*height, cos(angle)*height);
8296 if (biteTimer < biteTimerMax)
8302 biteLeftEmitter.stop();
8303 biteRightEmitter.stop();
8304 biteTimer = biteTimerMax;
8307 if (biteTimer > biteTimerBiteRange)
8309 biteLeftEmitter.stop();
8310 biteRightEmitter.stop();
8314 std::ostringstream os;
8315 os << "biteTimer: " << biteTimer;
8319 if (isInputEnabled())
8321 if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "urchin") == 0)
8323 if (!isEntityDead() && health > 0)
8326 if (urchinDelay < 0)
8330 Shot *s = dsq->game->fireShot("urchin", this, 0, position + offset);
8335 if (dsq->continuity.form == FORM_NORMAL && nocasecmp(dsq->continuity.costume, "jelly")==0)
8337 if (!isEntityDead() && health > 0)
8339 if (health < (maxHealth*JELLYCOSTUME_HEALTHPERC))
8344 jellyDelay = JELLYCOSTUME_HEALDELAY;
8353 dsq->game->spawnManaBall(position + offset + d, JELLYCOSTUME_HEALAMOUNT);
8355 //Shot *s = dsq->game->fireShot("urchin", this, 0, getWorldPosition());
8362 if (dsq->continuity.form == FORM_BEAST && bone_head && biteTimer < biteTimerBiteRange && biteTimer > 0)
8367 biteDelay = biteDelayPeriod;
8369 Vector p = bone_head->getWorldPosition();
8370 std::string shot = "Bite";
8371 if (dsq->continuity.biteMult > 1)
8375 Shot *s = dsq->game->fireShot(shot, this, 0, p);
8376 //s->setAimVector(getNormal());
8380 if (dsq->continuity.form == FORM_FISH && dsq->continuity.fishPoisonTimer.isActive())
8382 if (!vel.isLength2DIn(16))
8384 static float fishPoison = 0;
8386 if (fishPoison > 0.2)
8389 Shot *s = dsq->game->fireShot("FishPoison", this, 0, position);
8394 if (!(state.lockedToWall || state.dodgeEffectTimer.isActive()) && _isUnderWater && dsq->continuity.getWorldType() == WT_NORMAL && canMove)
8398 //debugLog("bursting~!");
8399 burst -= dt * BURST_USE_RATE;
8403 std::ostringstream os;
8404 os << "burst: " << burst;
8415 if (inputEnabled && _isUnderWater)
8419 // disable check to stop burst
8421 if (!isActing("a1"))
8425 //burstDelay = BURST_DELAY;
8426 //animatedBurst = false;
8430 else if (burstDelay > 0)
8433 if (burstDelay <= 0)
8438 burst += BURST_RECOVER_RATE * dt;
8446 //check to make sure there's still a wall there, if not fall off
8447 if (state.lockedToWall && !state.crawlingOnWall)
8449 rotateToVec(wallPushVec, dt*2);
8450 if (!boneLock.on && !dsq->game->isObstructed(wallLockTile))
8452 //debugLog("Dropping from wall");
8457 if (getState() != STATE_PUSH && !state.lockedToWall && inputEnabled && !ignoreInputDelay && _isUnderWater && canMove)
8460 Vector lastVel = vel;
8463 bool isMovingSlow = false;
8464 static Vector lastMousePos;
8465 Vector pos = lastMousePos - dsq->getGameCursorPosition();
8466 static bool lastDown;
8467 //int maxMouse = 200;
8470 //dsq->continuity.toggleMoveMode &&
8471 //!dsq->continuity.toggleMoveMode &&
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))))
8477 //addVec = getVectorToCursorFr
8478 //(dsq->inputMode != INPUT_JOYSTICK && dsq->inputMode != INPUT_KEYBOARD)
8479 if (dsq->inputMode == INPUT_MOUSE || !this->singing)
8481 addVec = getVectorToCursorFromScreenCentre();//getVectorToCursor();
8483 if (dsq->inputMode == INPUT_MOUSE)
8485 static Vector lastAddVec;
8486 if (!isActing(ACTION_PRIMARY) && bursting)
8488 addVec = lastAddVec;
8493 lastAddVec = addVec;
8497 if (addVec.isLength2DIn(minMouse))
8499 if (dsq->inputMode == INPUT_JOYSTICK)
8500 addVec = Vector(0,0,0);
8502 if (dsq->inputMode == INPUT_JOYSTICK && !core->mouse.buttons.left)
8504 addVec = Vector(0,0,0);
8512 if (!core->mouse.buttons.left && bursting)
8518 if (!addVec.isLength2DIn(minMouse))
8520 //if (core->mouse.buttons.left)
8522 len = addVec.getLength2D();
8524 addVec.setLength2D(a *10);
8526 addVec.setLength2D(a *2);
8528 addVec.setLength2D(a);
8530 addVec *= dsq->continuity.speedMult;
8533 if (len < maxMouse && !bursting)
8535 isMovingSlow = true;
8542 if (addVec.isLength2DIn(STOP_DISTANCE))
8546 rotation.interpolateTo(Vector(0,0,0), 0.1);
8547 if (vel.isLength2DIn(50))
8554 //vel = Vector(0,0,0);
8556 addVec = Vector(0,0,0);
8562 if (pos.getSquaredLength2D() > 10000)
8571 lastDown = core->mouse.buttons.left;
8574 std::ostringstream os;
8575 os << "addVec(" << addVec.x << ", " << addVec.y << ")";
8578 lastMousePos = dsq->getGameCursorPosition();
8580 if (!rolling && !state.backFlip && !flourish)
8582 bool swimOnBack = false;
8613 // HACK: joystick code / slow
8614 if (addVec.x == 0 && addVec.y == 0)
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);
8625 // will not get here if not underwater
8628 if ((addVec.x != 0 || addVec.y != 0))
8633 //float cheatLen = vel.getSquaredLength2D();
8636 Vector add = addVec;
8638 // HACK: this will let the player boost in one direction while turning to face another
8639 if (!core->mouse.buttons.left)
8644 add.setLength2D(BURST_ACCEL*dt);
8647 if (pushingOffWallEffect > 0 || wallJumps > 0)
8648 currentMaxSpeed = vars->maxWallJumpBurstSpeed + 50*wallJumps;
8650 currentMaxSpeed = vars->maxBurstSpeed;
8655 if (pushingOffWallEffect > 0)
8656 currentMaxSpeed = vars->maxWallJumpSpeed;
8657 else if (state.dodgeEffectTimer.isActive())
8658 currentMaxSpeed = vars->maxDodgeSpeed;
8661 if (isActing(ACTION_SLOW) || isMovingSlow)
8664 int spdRange = maxMouse - minMouse;
8665 float p = (len - minMouse) / spdRange;
8666 int spd = p * vars->maxSwimSpeed;// + minMouse
8667 currentMaxSpeed = spd;
8669 currentMaxSpeed = vars->maxSlowSwimSpeed;
8671 //else if (dsq->continuity.getWorldType() == WT_NORMAL)
8673 currentMaxSpeed = vars->maxSwimSpeed;
8676 currentMaxSpeed = vars->maxDreamWorldSpeed;
8682 if (dsq->continuity.form == FORM_SPIRIT)
8683 currentMaxSpeed *= 0.5;
8688 currentMaxSpeed -= leaches*60;
8689 // vel |= vel.getLength2D()-1*leaches;
8693 currentMaxSpeed -= 100;
8695 if (currentMaxSpeed < 0)
8696 currentMaxSpeed = 1;
8698 if (ropeState == 2 && currentMaxSpeed < vars->maxWallJumpBurstSpeed)
8699 currentMaxSpeed = vars->maxWallJumpBurstSpeed;
8705 currentMaxSpeed = 1200;
8714 if (getState() == STATE_TRANSFORM)
8715 rotateToVec(addVec, 0.1, 90);
8720 // here for roll key?
8721 // seems like this isn't reached
8722 //if (isActing("roll"))
8723 if (isActing(ACTION_ROLL))
8730 if (dsq->inputMode == INPUT_KEYBOARD)
8732 rotateToVec(addVec, t);
8735 else if (bursting && flourish)
8742 if (bursting && !core->mouse.buttons.left)
8746 rotateToVec(addVec, 0.1);
8748 if (!state.nearWall && !flourish)
8749 rotateToVec(addVec, 0.1);
8754 if ((!swimming || (swimming && !bursting && skeletalSprite.getCurrentAnimation()->name != "swim")) && !state.lockedToWall)
8757 //Animation *a = skeletalSprite.getCurrentAnimation();
8759 if (getState() == STATE_IDLE && !rolling)
8761 skeletalSprite.transitionAnimate("swim", ANIM_TRANSITION, -1);
8763 //animate(anim_swim);
8765 skeletalSprite.setTimeMultiplier(1);
8766 Animation *anim=skeletalSprite.getCurrentAnimation();
8767 if (!bursting && (anim && anim->name == "swim"))
8769 float velLen = vel.getLength2D();
8770 float time = velLen / 1200.0f;
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);
8781 if (currentAnim != getBurstAnimName() && skeletalSprite.getCurrentAnimation()->name != getBurstAnimName() && !state.lockedToWall)
8783 if (getState() == STATE_IDLE && !rolling)
8784 skeletalSprite.transitionAnimate(getBurstAnimName(), ANIM_TRANSITION);
8785 //animate(anim_burst);
8786 animatedBurst = true;
8792 int currentSwimSpeed = 400;
8793 //if (dsq->continuity.getWorldType() == WT_SPIRIT)
8795 if (dsq->continuity.form == FORM_SPIRIT)
8797 currentSwimSpeed *= 0.3;
8800 if (!_isUnderWater && !state.lockedToWall)
8802 //currentSwimSpeed *= 1.5;
8804 // base on where the mouse is
8807 addVec = getVectorToCursorFromScreenCentre();
8808 addVec.setLength2D(a);
8812 float fallMod = 1.5;
8813 if (dsq->continuity.form == FORM_SPIRIT)
8817 vel += Vector(0,980)*dt*fallMod;
8819 if (!rolling && !state.backFlip && !flourish)
8821 if (vel.x != 0 || vel.y != 0)
8822 rotateToVec(vel, 0.1);
8835 if (rolling && !state.backFlip)
8837 Vector v = getVectorToCursorFromScreenCentre();
8838 rotateToVec(v, 0.01);
8843 if (isActing("left"))
8844 addVec += Vector(-a, 0);
8845 if (isActing("right"))
8846 addVec += Vector(a, 0);
8850 addVec += Vector(0, -a);
8851 if (isActing("down"))
8852 addVec += Vector(0, a);
8863 if (dsq->continuity.form == FORM_FISH)
8864 rotation.interpolateTo(0, 0.2);
8867 //vel += -vel*0.999f*dt;
8871 std::ostringstream os;
8872 os << "fric(" << vel.x << ", " << vel.y;
8884 if (_isUnderWater && isInCurrent())
8886 if (dsq->loops.current == BBGE_AUDIO_NOCHANNEL)
8889 play.name = "CurrentLoop";
8894 dsq->loops.current = core->sound->playSfx(play);
8899 if (dsq->loops.current != BBGE_AUDIO_NOCHANNEL)
8901 core->sound->fadeSfx(dsq->loops.current, SFT_OUT, 1);
8902 dsq->loops.current = BBGE_AUDIO_NOCHANNEL;
8906 if (!swimming && _isUnderWater)
8909 currentMaxSpeed = vars->maxSwimSpeed;
8911 if (ropeState == 2 && currentMaxSpeed < vars->maxWallJumpBurstSpeed)
8912 currentMaxSpeed = vars->maxWallJumpBurstSpeed;
8914 if (!state.lockedToWall && !bursting)
8916 if (getState() == STATE_IDLE && inputEnabled)
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);
8923 idleAnimDelay -= dt;
8924 if (idleAnimDelay <= 0)
8926 idleAnimDelay = 1.5;/*anim_idle.time*2;*/
8929 if (currentAction == IDLE && (!skeletalSprite.isAnimating() || skeletalSprite.getCurrentAnimation()->name=="swim"
8930 || skeletalSprite.getCurrentAnimation()->name=="a1"))
8933 if (currentAction == STATE_IDLE)
8935 skeletalSprite.transitionAnimate("idle", ANIM_TRANSITION);
8938 //animate(anim_idle);
8944 if (_isUnderWater && fallGravityTimer)
8946 fallGravityTimer -= dt;
8947 currentMaxSpeed = lastOutOfWaterMaxSpeed;
8948 if (fallGravityTimer < 0)
8949 fallGravityTimer = 0;
8958 //static bool lastSpreadUp = false;
8959 if (!rolling && !internalOffset.isInterpolating())
8961 int spread = 8, rotSpread = 45;
8964 internalOffset = Vector(-spread, 0);
8965 internalOffset.interpolateTo(Vector(spread, 0), t, -1, 1, 1);
8968 rotationOffset = Vector(-rotSpread, 0);
8969 rotationOffset.interpolateTo(Vector(rotSpread, 0), t, -1, 1, 1);
8972 for (int i = 0; i < int((t*0.5)/0.01); i++)
8974 internalOffset.update(0.01);
8975 //rotationOffset.update(0.01);
8979 internalOffset.interpolateTo(Vector(spread, 0), t, 1, 1, 1);
8981 internalOffset.interpolateTo(Vector(-spread, 0), t, 1, 1, 1);
8984 //lastSpreadUp = !lastSpreadUp;
8985 //internalOffset.update(t*0.5);
8988 if (dsq->continuity.form != FORM_ENERGY && dsq->continuity.form != FORM_DUAL && dsq->continuity.form != FORM_FISH)
8990 if (leaches <= 0 && !bursting && !skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
8992 state.swimTimer += dt;
8994 if (state.swimTimer > 5)
8996 state.swimTimer = 0 - rand()%3;
8997 static int lastSwimExtra = -1;
8999 int anim = rand()%maxAnim;
9000 if (anim == lastSwimExtra)
9002 if (anim >= maxAnim)
9004 lastSwimExtra = anim;
9007 std::ostringstream os;
9008 os << "swimExtra-" << anim;
9009 skeletalSprite.transitionAnimate(os.str(), 0.5, 0, 6);
9019 if (!swimming || rolling)
9021 //state.swimTimer = 0;
9022 if (skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->animating)
9024 skeletalSprite.getAnimationLayer(ANIMLAYER_UPPERBODYIDLE)->stopAnimation();
9026 internalOffset.interpolateTo(Vector(0,0),0.5);
9030 //if (core->getNestedMains()==1)
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();
9036 if (cheatLen > sqr(250) && _isUnderWater && !state.lockedToWall)
9038 if (!swimEmitter.isRunning())
9039 swimEmitter.start();
9046 Vector targetScale(1,1);
9048 if (zoomOverriden || isEntityDead() || core->globalScale.isInterpolating())
9054 if (dsq->game->waterLevel.x > 0 && fabs(avatar->position.y - dsq->game->waterLevel.x) < 800)
9057 if (!myZoom.interpolating || (core->globalScale.target != zoomSurface && myZoom.timePeriod != time))
9059 myZoom.interpolateTo(zoomSurface, time, 0, 0, 1);
9062 else if (avatar->looking == 2)
9065 if (!myZoom.interpolating || (core->globalScale.target != zoomNaija && myZoom.timePeriod != time))
9068 std::ostringstream os;
9069 os << "zooming in on Naija: " << zoomNaija.x;
9072 myZoom.interpolateTo(zoomNaija, time, 0, 0, 1);
9075 else if ((cheatLen > sqr(250) && cheatLen < sqr(1000)) || attachedTo || avatar->looking==1)
9078 if (avatar->looking)
9082 if (!myZoom.interpolating || (core->globalScale.target != zoomMove && myZoom.timePeriod != time))
9083 myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
9085 else if (cheatLen < sqr(210) && !state.lockedToWall && stillTimer.getValue() > 4 && !isSinging())
9088 if (!myZoom.interpolating || (myZoom.target != zoomStop && myZoom.timePeriod != time))
9089 myZoom.interpolateTo(zoomStop, time, 0, 0, 1);
9091 else if (cheatLen >= sqr(1000))
9094 if (!myZoom.interpolating || (myZoom.target != zoomMove && myZoom.timePeriod != time))
9095 myZoom.interpolateTo(zoomMove, time, 0, 0, 1);
9098 if (myZoom.x < game->maxZoom)
9100 core->globalScale.x = game->maxZoom;
9101 core->globalScale.y = game->maxZoom;
9105 core->globalScale.x = myZoom.x;
9106 core->globalScale.y = myZoom.y;
9111 if (state.dodgeEffectTimer.isActive())
9116 if (!state.lockedToWall && !bursting && _isUnderWater && swimming && !isFollowingPath())
9118 //debugLog("collision avoidance");
9119 if (dsq->continuity.form == FORM_FISH)
9120 doCollisionAvoidance(dt, 1, 0.1, 0, 800, OT_HURT);
9122 doCollisionAvoidance(dt, 2, 1.0, 0, 800, OT_HURT);
9125 // friction for extraVel
9126 if (!extraVel.isZero())
9128 Vector d = extraVel;
9134 if (!game->isShuttingDownGameState())
9141 vel2 = Vector(0,0,0);
9144 //int collideCircle = 24;//24; // 48
9145 int collideCircle = 10;
9146 if (dsq->continuity.form == FORM_FISH)
9148 // just for external access
9149 // HACK: should always be using collide radius :| ?
9152 collideRadius = collideCircle;
9153 if (!state.lockedToWall && !isFollowingPath() && !riding)
9157 if (vel.getLength2D() < sqr(2))
9159 vel = Vector(0,0,0);
9162 if (!isInputEnabled() && isv(EV_NOINPUTNOVEL, 1))
9164 vel2=vel=Vector(0,0);
9168 moveVel = getMoveVel();
9170 if (!moveVel.isZero())
9172 bool collided = false;
9175 std::ostringstream os;
9176 os << "vel (" << vel.x << ", " << vel.y << ")";
9179 Vector mov = (moveVel * dt) + (extraVel * dt);
9181 mov.capLength2D(TILE_SIZE);
9183 if (mov.getSquaredLength2D() > sqr(TILE_SIZE))
9184 mov.setLength2D(TILE_SIZE);
9186 if (omov.getSquaredLength2D() > 0)
9188 while (omov.getSquaredLength2D() > 0)
9190 if (omov.getSquaredLength2D() < sqr(TILE_SIZE))
9198 lastLastPosition = position;
9199 lastPosition = position;
9200 Vector newPosition = position + mov;
9201 //Vector testPosition = position + (vel *dt)*2;
9202 position = newPosition;
9205 int hw = collideCircle;
9208 if (dsq->game->collideCircleWithGrid(position, hw, &fix))
9210 if (dsq->game->lastCollideTileType == OT_HURT
9211 && dsq->continuity.getWorldType() != WT_SPIRIT
9212 && dsq->continuity.form != FORM_NATURE)
9217 vel2 = Vector(0,0,0);
9218 //doCollisionAvoidance(1, 3, 1);
9220 Vector v = dsq->game->getWallNormal(position);
9229 if (currentState == STATE_PUSH)
9231 dsq->sound->playSfx("rockhit");
9232 dsq->spawnParticleEffect("rockhit", position);
9236 d.damage = pushDamage;
9239 setState(STATE_IDLE);
9246 position = lastPosition;
9249 // this is the out of water bounce...
9251 //debugLog("above water bounce");
9255 Vector n = getWallNormal(TileVector(lastPosition));
9256 n *= vel.getLength2D();
9259 std::ostringstream os;
9260 os << "vel(" << vel.x << ", " << vel.y << ") n(" << n.x << ", " << n.y << ")";
9268 //vel = (vel*0.5 + n*0.5);
9271 //debugLog("Vel less than 0");
9276 //debugLog("Vel was 0");
9277 vel = getWallNormal(TileVector(position));
9278 vel.setLength2D(500);
9281 if (vel.isLength2DIn(500))
9282 vel.setLength2D(500);
9285 vel.capLength2D(800);
9287 position = lastPosition;
9288 this->doCollisionAvoidance(1, 4, 0.5, 0, 500);
9295 position = lastPosition;
9300 float len = vel.getLength2D();
9301 position = lastPosition;
9303 // works as long as not buried in wall ... yep. =(
9304 if (dsq->game->isObstructed(TileVector(position)))
9311 // && dsq->game->getPercObsInArea(position, 4) < 0.75
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)
9333 if (!_isUnderWater && dsq->continuity.form == FORM_FISH)
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)
9348 //n.setLength2D(len/2);
9352 //vel = (n + vel)*0.5;
9364 if (!collided && checkWarpAreas()) collided = true;
9365 if (collided) break;
9367 // DO NOT COLLIDE WITH LR_ENTITIES
9368 // ENTITY SCRIPTS WILL HANDLE Entity V. Entity COLLISIONS
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)
9384 if (isNearObstruction(4))
9387 Vector n = dsq->game->getWallNormal(position, 6);
9390 Vector add = n * 100;
9391 Vector f = getForward();
9401 if (ignoreInputDelay>0)
9403 ignoreInputDelay -= dt;
9404 if (ignoreInputDelay < 0)
9405 ignoreInputDelay = 0;
9408 if (burstBar && burstBar->alpha == 1)
9410 float amount = burst;
9411 if (amount > 1) amount = 1;
9412 if (amount < 0) amount = 0;
9413 burstBar->frame = (19-(amount*19));
9421 if (riding || boneLock.on)
9425 applyRidingPosition();
9427 chargeEmitter->position = chargingEmitter->position = position + offset;
9430 if (leftHandEmitter && rightHandEmitter && boneLeftHand && boneRightHand)
9432 leftHandEmitter->position = boneLeftHand->getWorldCollidePosition(Vector(0, 16));
9433 rightHandEmitter->position = boneRightHand->getWorldCollidePosition(Vector(0,16));
9436 dsq->game->handleShotCollisions(this, (activeAura == AURA_SHIELD));
9440 void Avatar::checkNearWall()
9442 state.nearWall = false;
9444 if (!inCurrent && bursting && !state.lockedToWall && !vel.isZero() && !riding && _isUnderWater)
9452 Vector n = dsq->game->getWallNormal(position, 8);
9455 state.nearWall = true;
9457 rotateToVec(n, t, 0);
9458 skeletalSprite.transitionAnimate("wall", t);
9461 state.nearWall = false;
9463 int checkRange = 11;
9466 TileVector oT(position);
9467 TileVector t=oT,lastT=oT;
9469 for (int i = 1; i < checkRange; i++)
9473 if (dsq->game->isObstructed(t) && dsq->game->getGrid(t) != OT_HURT)
9482 Vector n = dsq->game->getWallNormal(t.worldVector());
9485 state.nearWall = true;
9487 rotateToVec(n, t, 0);
9488 skeletalSprite.transitionAnimate("wall", t);
9491 state.nearWall = false;
9495 state.nearWall = false;
9500 void Avatar::checkSpecial()
9503 if (dsq->continuity.getFlag("VedhaFollow1") == 7)
9505 int total = 0, c = 0;
9506 for (int i = 0; i < dsq->entities.size(); i++)
9508 Entity *e = dsq->entities[i];
9509 if (e->name == "PracticeEnemy")
9512 if (e->isEntityDead())
9519 dsq->continuity.setFlag("VedhaFollow1", 8);
9520 dsq->getEntityByName("Vedha")->activate();
9526 void Avatar::checkConvAreas()
9528 if (core->getNestedMains() != 1) return;
9529 if (game->avatar->disableConversationStart) return;
9531 for (int i = 0; i < dsq->game->convAreas.size(); i++)
9533 ConvArea * a = &dsq->game->convAreas[i];
9534 Vector diff = a->position - this->position;
9535 if (diff.getSquaredLength2D() < sqr(a->radius))
9537 if (dsq->continuity.getFlag(a->conv)==0)
9539 dsq->runScript(a->conv);
9545 Entity *entityRun = 0;
9549 if (e->activationType == Entity::ACT_RANGE)
9551 Vector diff = e->position - this->position;
9554 (e->canTalkWhileMoving || !e->position.isInterpolating())
9555 && e->activationType == Entity::ACT_RANGE
9556 && diff.getSquaredLength2D() < sqr(e->activationRange)
9560 if (lastEntityActivation != e)
9564 //dsq->runScript(e->convo);
9567 int range = e->activationRange + 40;
9568 if (diff.getSquaredLength2D() < sqr(range))
9570 push |= range - diff.getLength2D();
9571 dsq->game->avatar->vel = -push*2;
9578 lastEntityActivation = entityRun;
9581 void Avatar::onWarp()
9583 avatar->setv(EV_NOINPUTNOVEL, 0);
9584 closeSingingInterface();
9587 bool Avatar::checkWarpAreas()
9590 for (i = 0; i < dsq->game->paths.size(); i++)
9593 Path *p = dsq->game->paths[i];
9594 if (!p->nodes.empty())
9596 PathNode *n = &p->nodes[0];
9600 if (!p->vox.empty())
9602 if (p->isCoordinateInside(position))
9604 if (p->replayVox == 1)
9611 dsq->voiceOnce(p->vox);
9615 if (!p->warpMap.empty() && p->pathType == PATH_WARP)
9622 if ((position - n->position).getSquaredLength2D() < sqr(range))
9629 if (p->isCoordinateInside(position))
9632 backPos = p->getBackPos(position);
9640 if (avatar->canWarp)
9641 dsq->game->warpToSceneFromNode(p);
9644 avatar->position = backPos;
9645 //avatar->vel = -avatar->vel * 0.5;
9646 Vector n = p->getEnterNormal();
9647 n.setLength2D(avatar->vel.getLength2D());
9653 if (core->getNestedMains() == 1 && !riding)
9655 if (p->warpMap.empty() && !p->warpNode.empty())
9657 if (p->isCoordinateInside(position))
9659 Path *p2 = dsq->game->getPathByName(p->warpNode);
9662 dsq->game->preLocalWarp(p->localWarpType);
9663 dsq->game->avatar->position = p2->getPathNode(0)->position;
9664 dsq->game->postLocalWarp();
9676 for (i = 0; i < dsq->game->warpAreas.size(); i++)
9678 WarpArea *a = &dsq->game->warpAreas[i];
9681 Vector diff = a->position - this->position;
9682 if (diff.getSquaredLength2D() < sqr(a->radius))
9686 dsq->game->warpToArea(a);
9691 position = lastPosition;
9699 if (position.x > a->position.x - a->w && position.x < a->position.x + a->w)
9701 if (position.y > a->position.y - a->h && position.y < a->position.y + a->h)
9705 dsq->game->warpToArea(a);
9710 position = lastPosition;
9720 void Avatar::startConversation()
9722 closeSingingInterface();