1 /*
2 Copyright (C) 2007, 2010 - Bit-Blot
3
4 This file is part of Aquaria.
5
6 Aquaria is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 #include "../BBGE/MathFunctions.h"
22
23 #include "Entity.h"
24 #include "DSQ.h"
25 #include "Game.h"
26 #include "Avatar.h"
27 #include "ScriptedEntity.h"
28 #include "Shot.h"
29 #include "PathFinding.h"
30
31 //Shader Entity::blurShader;
32
stopPull()33 void Entity::stopPull()
34 {
35 if (dsq->game->avatar->pullTarget == this)
36 {
37 dsq->game->avatar->pullTarget = 0;
38 }
39 }
40
setIngredientData(const std::string & name)41 void Entity::setIngredientData(const std::string &name)
42 {
43 ingredientData = dsq->continuity.getIngredientDataByName(name);
44 }
45
entityDied(Entity * e)46 void Entity::entityDied(Entity *e)
47 {
48 for (int i = 0; i < targets.size(); i++)
49 {
50 targets[i] = 0;
51 }
52
53 if (boneLock.on)
54 {
55 if (boneLock.entity == e)
56 {
57 setBoneLock(BoneLock());
58 }
59 }
60 }
61
setBounceType(BounceType bt)62 void Entity::setBounceType(BounceType bt)
63 {
64 bounceType = bt;
65 }
66
getBounceType()67 BounceType Entity::getBounceType()
68 {
69 return bounceType;
70 }
71
generateCollisionMask(int ovrCollideRadius)72 void Entity::generateCollisionMask(int ovrCollideRadius)
73 {
74 if (this->skeletalSprite.isLoaded())
75 {
76 for (int i = 0; i < skeletalSprite.bones.size(); i++)
77 {
78 if (skeletalSprite.bones[i]->generateCollisionMask)
79 {
80 dsq->game->generateCollisionMask(skeletalSprite.bones[i], ovrCollideRadius);
81 }
82 }
83 }
84 }
85
86 /*
87 void Entity::clearv()
88 {
89 for (int i = 0; i < EV_MAX; i++)
90 {
91 vs[i] = 0;
92 }
93 }
94 */
95
setBoneLock(const BoneLock & boneLock)96 bool Entity::setBoneLock(const BoneLock &boneLock)
97 {
98 if (!canSetBoneLock()) return false;
99
100 if (boneLock.on && boneLockDelay > 0) return false;
101 if (this->boneLock.on && boneLock.on) return false;
102
103 if (this->boneLock.on && !boneLock.on)
104 {
105 boneLockDelay = 0.1;
106 this->boneLock = boneLock;
107 }
108 else
109 {
110 if (!boneLock.entity)
111 return false;
112
113 if (boneLock.entity && !boneLock.bone)
114 {
115 this->boneLock = boneLock;
116 this->boneLock.circleOffset = this->position - (boneLock.entity->getWorldPosition());
117 this->boneLock.circleOffset.setLength2D(boneLock.entity->collideRadius);
118 this->boneLock.origRot = boneLock.entity->rotation.z;
119 /*
120 this->boneLock = boneLock;
121 this->boneLock.localOffset = this->position - (boneLock.entity->getWorldPosition());
122 this->boneLock.localOffset = boneLock.entity->getInvRotPosition(this->boneLock.localOffset);
123 this->boneLock.circleOffset = this->position - (boneLock.entity->position);
124 this->boneLock.circleOffset.setLength2D(boneLock.entity->collideRadius);
125 this->boneLock.origRot = boneLock.entity->getWorldRotation();
126 //this->boneLock.origRot = MathFunctions::toRadians(this->boneLock.origRot);
127 MathFunctions::calculateAngleBetweenVectorsInRadians(boneLock.entity->position + boneLock.entity->getForward(), boneLock.entity->position, this->boneLock.origRot);
128 //position, boneLock.entity->position,
129 MathFunctions::calculateAngleBetweenVectorsInRadians(position, boneLock.entity->position, this->boneLock.offRot);
130
131 while (this->boneLock.origRot > PI)
132 this->boneLock.origRot -= PI;
133 while (this->boneLock.origRot < 0)
134 this->boneLock.origRot += PI;
135 while (this->boneLock.offRot > PI)
136 this->boneLock.offRot -= PI;
137 while (this->boneLock.offRot < 0)
138 this->boneLock.offRot += PI;
139 //this->boneLock.offRot = atanf(this->boneLock.circleOffset.y / this->boneLock.circleOffset.x);
140 //
141 //this->boneLock.localOffset = boneLock.bone->getOriginCollidePosition(this->boneLock.localOffset);
142 */
143
144 }
145 else
146 {
147 this->boneLock = boneLock;
148 //this->boneLock.localOffset = this->position - boneLock.bone->getWorldPosition();
149 this->boneLock.localOffset = this->position - (boneLock.bone->getWorldPosition());
150 this->boneLock.localOffset = boneLock.bone->getInvRotPosition(this->boneLock.localOffset);
151 this->boneLock.origRot = boneLock.bone->getWorldRotation();
152 //this->boneLock.localOffset = boneLock.bone->getOriginCollidePosition(this->boneLock.localOffset);
153
154 }
155 }
156
157 setv(EV_BONELOCKED, boneLock.on);
158
159 onSetBoneLock();
160
161 return true;
162 }
163
canSetBoneLock()164 bool Entity::canSetBoneLock()
165 {
166 return true;
167 }
168
Entity()169 Entity::Entity()
170 {
171 addType(SCO_ENTITY);
172 poison = 0.0f;
173 calledEntityDied = false;
174 wasUnderWater = true;
175 waterBubble = 0;
176 ridingFlip = false;
177 ridingRotation = 0;
178 boneLockDelay = 0;
179 ingredientData = 0;
180 for (int i = 0; i < EV_MAX; i++)
181 {
182 vs[i] = 0;
183 }
184 setv(EV_COLLIDELEVEL, 1);
185 setv(EV_LOOKAT, 1);
186 setv(EV_SWITCHCLAMP, 1);
187 setvf(EV_CLAMPTRANSF, 0.2);
188 setv(EV_FLIPTOPATH, 1);
189 setv(EV_NOINPUTNOVEL, 1);
190 setv(EV_VINEPUSH, 1);
191 setv(EV_BEASTBURST, 1);
192 setv(EV_WEBSLOW, 100);
193 //debugLog("Entity::Entity()");
194 //clampOnSwitchDir = true;
195 //registerEntityDied = false;
196 invincible = false;
197 lanceDelay = 0;
198 lance = 0;
199 lanceTimer = 0;
200 lanceGfx = 0;
201 lanceBone = 0;
202 beautyFlip = true;
203 fhScale = fvScale = 0;
204 flipScale = Vector(1,1);
205 wasUnderWater = true;
206 deathScene = false;
207 dieTimer = 0;
208 bounceType = BOUNCE_SIMPLE;
209 riding = 0;
210 eatType = EAT_DEFAULT;
211 stickToNaijasHead = false;
212 spiritFreeze = true;
213 pauseFreeze = true;
214 canLeaveWater = false;
215 targetPriority = 0;
216 //renderPass = RENDER_ALL;
217 //crawling = 0;
218 ridingOnEntity = 0;
219 targetRange = 32;
220 //energyChargeTarget = energyShotTarget = true;
221 deathSound = "GenericDeath";
222 entityID = 0;
223 //assignUniqueID();
224 hair = 0;
225 maxSpeedLerp = 1;
226 fillGridFromQuad = false;
227 dropChance = 0;
228 inCurrent = false;
229 entityProperties.resize(EP_MAX);
230 for (int i = 0; i < entityProperties.size(); i++)
231 {
232 entityProperties[i] = false;
233 }
234 entityTypeIdx = -1;
235 damageTime = vars->entityDamageTime;
236 slowingToStopPathTimer = 0;
237 slowingToStopPath = 0;
238 followPos = 0;
239 watchingEntity = 0;
240 swimPath = false;
241 currentEntityTarget = 0;
242 deleteOnPathEnd = false;
243 multColor = Vector(1,1,1);
244 collideRadius = 24;
245 entityType = EntityType(0);
246 targets.resize(10);
247 attachedTo = 0;
248 //target = 0;
249 frozenTimer = 0;
250 canBeTargetedByAvatar = false;
251 activationRange = 0;
252 activationType = ACT_NONE;
253 pushDamage = 0;
254
255 //debugLog("dsq->addEntity()");
256
257 dsq->addEntity(this);
258 maxSpeed = 300;
259 entityDead = false;
260 health = maxHealth = 5;
261 invincibleBreak = false;
262 activationRadius = 40;
263 activationRange = 600;
264 //affectedBySpells = true;
265 // followAvatar = false;
266 followEntity = 0;
267 bubble = 0;
268
269 /*
270 copySkel.parentManagedStatic = 1;
271 copySkel.updateAfterParent = 1;
272 addChild(©Skel);
273 */
274
275 //debugLog("skeletalSprite init");
276
277 skeletalSprite.updateAfterParent = 1;
278 skeletalSprite.setAnimationKeyNotify(this);
279 addChild(&skeletalSprite, PM_STATIC);
280
281 //debugLog("damageTarget stuff");
282
283 setDamageTarget(DT_AVATAR_NATURE, false);
284 setDamageTarget(DT_AVATAR_LIZAP, true);
285
286 setDamageTarget(DT_AVATAR_BUBBLE, false);
287 setDamageTarget(DT_AVATAR_SEED, false);
288
289 stopSoundsOnDeath = false;
290
291
292 //debugLog("End Entity::Entity()");
293 }
294
setDeathScene(bool v)295 void Entity::setDeathScene(bool v)
296 {
297 deathScene = v;
298 }
299
setCanLeaveWater(bool v)300 void Entity::setCanLeaveWater(bool v)
301 {
302 canLeaveWater = v;
303 }
304
checkSplash(const Vector & o)305 bool Entity::checkSplash(const Vector &o)
306 {
307 Path *lastWaterBubble = waterBubble;
308
309 Vector check = position;
310 if (!o.isZero())
311 check = o;
312 bool changed = false;
313 if (wasUnderWater && !isUnderWater(o))
314 {
315 sound("splash-outof");
316 changed = true;
317 wasUnderWater = false;
318 }
319 else if (!wasUnderWater && isUnderWater(o))
320 {
321 sound("splash-into");
322 changed = true;
323 wasUnderWater = true;
324 }
325
326 if (changed)
327 {
328 float angle;
329
330 if (!wasUnderWater && waterBubble)
331 {
332 Vector diff = position - waterBubble->nodes[0].position;
333 angle = MathFunctions::getAngleToVector(diff, 0) + 180;
334 }
335 else if (wasUnderWater && lastWaterBubble)
336 {
337 Vector diff = position - lastWaterBubble->nodes[0].position;
338 angle = MathFunctions::getAngleToVector(diff, 0);
339 }
340 else
341 {
342 angle = MathFunctions::getAngleToVector(vel+vel2, 0);
343 }
344 dsq->spawnParticleEffect("Splash", check, angle);
345 }
346
347 return changed;
348 }
349
setSpiritFreeze(bool v)350 void Entity::setSpiritFreeze(bool v)
351 {
352 spiritFreeze = v;
353 }
354
setPauseFreeze(bool v)355 void Entity::setPauseFreeze(bool v)
356 {
357 pauseFreeze = v;
358 }
359
setEntityProperty(EntityProperty ep,bool value)360 void Entity::setEntityProperty(EntityProperty ep, bool value)
361 {
362 entityProperties[int(ep)] = value;
363 }
364
isEntityProperty(EntityProperty ep)365 bool Entity::isEntityProperty(EntityProperty ep)
366 {
367 return entityProperties[int(ep)];
368 }
369
getRidingPosition()370 Vector Entity::getRidingPosition()
371 {
372 if (ridingPosition.isZero())
373 return position;
374 return ridingPosition;
375 }
376
getRidingRotation()377 float Entity::getRidingRotation()
378 {
379 if (ridingRotation == 0)
380 return rotation.z;
381 return ridingRotation;
382 }
383
384
setRidingFlip(bool on)385 void Entity::setRidingFlip(bool on)
386 {
387 ridingFlip = on;
388 }
389
getRidingFlip()390 bool Entity::getRidingFlip()
391 {
392 return ridingFlip;
393 }
394
setRidingData(const Vector & pos,float rot,bool fh)395 void Entity::setRidingData(const Vector &pos, float rot, bool fh)
396 {
397 setRidingPosition(pos);
398 setRidingRotation(rot);
399 setRidingFlip(fh);
400 }
401
setRidingPosition(const Vector & pos)402 void Entity::setRidingPosition(const Vector &pos)
403 {
404 ridingPosition = pos;
405 }
406
setRidingRotation(float rot)407 void Entity::setRidingRotation(float rot)
408 {
409 ridingRotation = rot;
410 }
411
doFriction(float dt)412 void Entity::doFriction(float dt)
413 {
414 if (vel.getSquaredLength2D() > 0)
415 {
416 const float velStopLen = 10;
417 bool wasIn = vel.isLength2DIn(velStopLen);
418
419 Vector d = vel;
420 d.setLength2D(vars->frictionForce);
421 vel -= d * dt;
422
423 if (!wasIn && vel.isLength2DIn(velStopLen))
424 vel = 0;
425 }
426 }
427
doFriction(float dt,float len)428 void Entity::doFriction(float dt, float len)
429 {
430 Vector v = vel;
431 if (!v.isZero())
432 {
433 v.setLength2D(dt * len);
434 vel -= v;
435 }
436 }
437
setName(const std::string & name)438 void Entity::setName(const std::string &name)
439 {
440 this->name = name;
441 }
442
followPath(Path * p,float speed,int dir,bool deleteOnEnd)443 float Entity::followPath(Path *p, float speed, int dir, bool deleteOnEnd)
444 {
445 if(!speed)
446 speed = getMaxSpeed();
447 deleteOnPathEnd = deleteOnEnd;
448
449 position.stopPath();
450 position.ensureData();
451 position.data->path.clear();
452 if (dir)
453 {
454 for (int i = p->nodes.size()-1; i >=0; i--)
455 {
456 PathNode pn = p->nodes[i];
457 position.data->path.addPathNode(pn.position, 1.0f-(float(i/float(p->nodes.size()))));
458 }
459 }
460 else
461 {
462 for (int i = 0; i < p->nodes.size(); i++)
463 {
464 PathNode pn = p->nodes[i];
465 position.data->path.addPathNode(pn.position, float(i/float(p->nodes.size())));
466 }
467 }
468 //debugLog("Calculating Time");
469 float time = position.data->path.getLength()/speed;
470 //debugLog("Starting");
471 position.data->path.getPathNode(0)->value = position;
472 position.startPath(time);//, 1.0f/2.0f);
473 return time;
474 }
475
moveToPos(Vector dest,float speed,int dieOnPathEnd,bool swim)476 float Entity::moveToPos(Vector dest, float speed, int dieOnPathEnd, bool swim)
477 {
478 if(!speed)
479 speed = getMaxSpeed();
480
481 Vector start = position;
482 followEntity = 0;
483 //watchingEntity = 0;
484
485 position.ensureData();
486 position.data->path.clear();
487 position.stop();
488
489 swimPath = swim;
490 //debugLog("Generating path to: " + path->name);
491 PathFinding::generatePath(this, TileVector(start), TileVector(dest));
492 //int sz = position.data->path.getNumPathNodes();
493 //position.data->path.addPathNode(path->nodes[0].position, 1);
494 //VectorPath old = position.data->path;
495 /*std::ostringstream os;
496 os << "Path length: " << sz;
497 debugLog(os.str());*/
498
499 //debugLog("Regenerating section");
500
501 //int ms = sz % 12;
502 /*
503 if (sz > 12)
504 {
505 int node = sz/2;
506 dsq->pathFinding.generatePath(this, TileVector(position), TileVector(position.path.getPathNode(node)->value));
507 old.splice(position.data->path, node);
508 position.data->path = old;
509 }
510 */
511 this->vel = 0;
512
513 //debugLog("Molesting Path");
514
515 PathFinding::molestPath(position.data->path);
516 //position.data->path.realPercentageCalc();
517 //position.data->path.cut(4);
518
519 //debugLog("forcing path to minimum 2 nodes");
520 PathFinding::forceMinimumPath(position.data->path, start, dest);
521 //debugLog("Done");
522
523 //debugLog("Calculating Time");
524 float time = position.data->path.getLength()/speed;
525 //debugLog("Starting");
526 position.data->path.getPathNode(0)->value = position;
527 position.startPath(time);//, 1.0f/2.0f);
528
529 /*
530 if (dieOnPathEnd)
531 position.endOfPathEvent.set(MakeFunctionEvent(Entity, safeKill));
532 */
533
534 //debugLog("Set delete on Path end");
535 deleteOnPathEnd = dieOnPathEnd;
536
537 //debugLog("End of Generate Path");
538
539 //position.startSpeedPath(dsq->continuity.getSpeedType(speedType));
540 //position.startPath(((position.data->path.getNumPathNodes()*TILE_SIZE*4)-2)/dsq->continuity.getSpeedType(speedType));
541
542 return time;
543 }
544
stopFollowingPath()545 void Entity::stopFollowingPath()
546 {
547 position.stopPath();
548 }
549
flipToTarget(Vector pos)550 void Entity::flipToTarget(Vector pos)
551 {
552 //if (dsq->game->avatar->position.x > r->position.x)
553 //else if (dsq->game->avatar->position.x < r->position.x)
554
555 if (pos.x > position.x)
556 {
557 if (!isfh())
558 flipHorizontal();
559 }
560 else if (pos.x < position.x)
561 {
562 if (isfh())
563 flipHorizontal();
564 }
565 }
566
getTargetEntity(int t)567 Entity* Entity::getTargetEntity(int t)
568 {
569 return targets[t];
570 }
571
setTargetEntity(Entity * e,int t)572 void Entity::setTargetEntity(Entity *e, int t)
573 {
574 targets[t] = e;
575 }
576
hasTarget(int t)577 bool Entity::hasTarget(int t)
578 {
579 return (targets[t]!=0);
580 }
581
watchEntity(Entity * e)582 void Entity::watchEntity(Entity *e)
583 {
584 watchingEntity = e;
585 }
586
destroy()587 void Entity::destroy()
588 {
589 if (stopSoundsOnDeath)
590 this->stopAllSounds();
591 this->unlinkAllSounds();
592
593 /*
594 if (hair)
595 {
596 hair->safeKill();
597 hair = 0;
598 }
599 */
600 if (hair)
601 {
602 // let the engine clean up hair
603 hair = 0;
604 }
605 Shot::targetDied(this);
606 dsq->removeEntity(this);
607 Quad::destroy();
608 }
609
checkSurface(int tcheck,int state,float statet)610 bool Entity::checkSurface(int tcheck, int state, float statet)
611 {
612 TileVector hitTile;
613 if (isNearObstruction(tcheck, OBSCHECK_8DIR, &hitTile))
614 {
615 if (clampToSurface(tcheck, Vector(0,0), hitTile))
616 {
617 setState(state, statet);
618 return true;
619 }
620 }
621 return false;
622 /*
623 checkSurfaceDelay = math.random(3)+1
624 if entity_clampToSurface(me, 0.5, 3) then
625 entity_setState(me, STATE_WALL, 2 + math.random(2))
626 else
627 checkSurfaceDelay = 0.1
628 end
629 */
630 }
631
rotateToSurfaceNormal(float t,int n,int rot)632 void Entity::rotateToSurfaceNormal(float t, int n, int rot)
633 {
634 Vector v;
635 if (ridingOnEntity)
636 {
637 v = position - ridingOnEntity->position;
638 }
639 else
640 {
641 if (n == 0)
642 v = dsq->game->getWallNormal(position);
643 else
644 v = dsq->game->getWallNormal(position, n);
645 }
646
647 if (!v.isZero())
648 {
649 rotateToVec(v, t, rot);
650 }
651 }
652
getv(EV ev)653 int Entity::getv(EV ev)
654 {
655 return vs[ev];
656 }
657
getvf(EV ev)658 float Entity::getvf(EV ev)
659 {
660 return float(vs[ev])*0.01f;
661 }
662
isv(EV ev,int v)663 bool Entity::isv(EV ev, int v)
664 {
665 return vs[ev] == v;
666 }
667
setv(EV ev,int v)668 void Entity::setv(EV ev, int v)
669 {
670 vs[ev] = v;
671 }
672
setvf(EV ev,float v)673 void Entity::setvf(EV ev, float v)
674 {
675 vs[ev] = int(v * 100.0f);
676 }
677
clampToSurface(int tcheck,Vector usePos,TileVector hitTile)678 bool Entity::clampToSurface(int tcheck, Vector usePos, TileVector hitTile)
679 {
680 float t = getvf(EV_CLAMPTRANSF);
681 if (usePos.isZero())
682 usePos = position;
683 if (tcheck == 0)
684 tcheck = 40;
685 bool clamped = false;
686 // HACK: ensure entity gets to location
687 //t = 0;
688 //setCrawling(true);
689 setv(EV_CRAWLING, 1);
690 burstTimer.stop();
691 // do stuff
692 Vector pos = TileVector(usePos).worldVector();
693 if (!hitTile.isZero())
694 {
695 //debugLog("using hitTile");
696 pos = hitTile.worldVector();
697 clamped = true;
698 }
699 else
700 {
701 //debugLog("NOT using hitTile");
702 if (vel.getSquaredLength2D() < 1)
703 {
704 longCheck:
705 //debugLog("LongCheck");
706 for (int i = 0; i < tcheck; i++)
707 {
708 int bit = i*TILE_SIZE;
709 int backBit = (i-1)*TILE_SIZE;
710 if (dsq->game->isObstructed(TileVector(pos - Vector(bit,0))))
711 {
712 TileVector t(pos - Vector(backBit,0));
713 pos = t.worldVector();
714 clamped = true;
715 break;
716 }
717 if (dsq->game->isObstructed(TileVector(pos - Vector(0,bit))))
718 {
719 TileVector t(pos - Vector(0,backBit));
720 pos = t.worldVector();
721 clamped = true;
722 break;
723 }
724 if (dsq->game->isObstructed(TileVector(pos + Vector(bit,0))))
725 {
726 TileVector t(pos + Vector(backBit,0));
727 pos = t.worldVector();
728 clamped = true;
729 break;
730 }
731 if (dsq->game->isObstructed(TileVector(pos + Vector(0,bit))))
732 {
733 TileVector t(pos + Vector(0,backBit));
734 pos = t.worldVector();
735 clamped = true;
736 break;
737 }
738 if (dsq->game->isObstructed(TileVector(pos + Vector(-bit,-bit))))
739 {
740 TileVector t(pos + Vector(-backBit,-backBit));
741 pos = t.worldVector();
742 clamped = true;
743 break;
744 }
745 if (dsq->game->isObstructed(TileVector(pos + Vector(-bit,bit))))
746 {
747 TileVector t(pos + Vector(-backBit,backBit));
748 pos = t.worldVector();
749 clamped = true;
750 break;
751 }
752 if (dsq->game->isObstructed(TileVector(pos + Vector(bit,-bit))))
753 {
754 TileVector t(pos + Vector(backBit,-backBit));
755 pos = t.worldVector();
756 clamped = true;
757 break;
758 }
759 if (dsq->game->isObstructed(TileVector(pos + Vector(bit,bit))))
760 {
761 TileVector t(pos + Vector(backBit,backBit));
762 pos = t.worldVector();
763 clamped = true;
764 break;
765 }
766 }
767 }
768 else
769 {
770 //debugLog("VelCheck");
771 Vector v = vel;
772 v.normalize2D();
773 for (int i = 0; i < tcheck; i++)
774 {
775 if (dsq->game->isObstructed(TileVector(pos + v*TILE_SIZE*i)))
776 {
777 TileVector t(pos + v*TILE_SIZE*(i-1));
778 pos = t.worldVector();
779 clamped = true;
780 break;
781 }
782 }
783 if (!clamped)
784 goto longCheck;
785 }
786 }
787 if (t > 0)
788 {
789 Vector n = dsq->game->getWallNormal(pos);
790 n *= getv(EV_WALLOUT);
791 //pos += n;
792 Vector diff = getWorldPosition() - pos;
793 //e->offset.interpolateTo(diff, t);
794 offset = diff;
795 offset.interpolateTo(n, t);
796 position = pos;
797
798 //e->rotateToSurfaceNormal(t);
799 rotateToSurfaceNormal(0);
800
801 setv(EV_CLAMPING, 1);
802
803 //e->position.interpolateTo(pos, t);
804 /*
805 debugLog("interpolating position");
806 e->position.interpolateTo(Vector(0,0,0), t);
807 */
808 }
809 else
810 position = pos;
811
812 if (clamped)
813 {
814 vel = Vector(0,0,0);
815 vel2 = Vector(0,0,0);
816 }
817
818 return clamped;
819 }
820
heal(float a,int type)821 void Entity::heal(float a, int type)
822 {
823 if (!entityDead)
824 {
825 health += a;
826 if (health > maxHealth) health = maxHealth;
827 onHealthChange(a);
828 onHeal(type);
829 }
830 }
831
revive(float a)832 void Entity::revive(float a)
833 {
834 entityDead = false;
835 health = 0;
836 heal(a);
837 if (getState() != STATE_IDLE)
838 perform(STATE_IDLE);
839 onHealthChange(a);
840 //health += a;
841 }
842
isGoingToBeEaten()843 bool Entity::isGoingToBeEaten()
844 {
845 return (eatType != EAT_NONE && (lastDamage.damageType == DT_AVATAR_BITE || lastDamage.damageType == DT_AVATAR_PETBITE));
846 }
847
doDeathEffects(float manaBallEnergy,bool die)848 void Entity::doDeathEffects(float manaBallEnergy, bool die)
849 {
850 if (deathScene || !isGoingToBeEaten())
851 {
852 if (manaBallEnergy)
853 {
854 if (chance(dropChance))
855 {
856 dsq->game->spawnManaBall(position, manaBallEnergy);
857 }
858 }
859 if (die)
860 {
861 setLife(1);
862 setDecayRate(4);
863 fadeAlphaWithLife = true;
864 }
865 else
866 {
867 alpha.interpolateTo(0.01,1);
868 }
869 }
870 else
871 {
872 if (die)
873 {
874 setLife(1);
875 setDecayRate(1);
876 fadeAlphaWithLife = true;
877 }
878 else
879 {
880 alpha.interpolateTo(0.01,1);
881 }
882 scale.interpolateTo(Vector(0,0), 1);
883 stickToNaijasHead = true;
884 }
885 activationType = ACT_NONE;
886
887 if (ingredientData)
888 {
889 dsq->game->spawnIngredientFromEntity(this, ingredientData);
890 }
891 }
892
isNearObstruction(int sz,int type,TileVector * hitTile)893 bool Entity::isNearObstruction(int sz, int type, TileVector *hitTile)
894 {
895 bool v=false;
896 TileVector t(position);
897 TileVector test;
898 switch(type)
899 {
900 case OBSCHECK_RANGE:
901 {
902 for (int x = -sz; x <= sz; x++)
903 {
904 for (int y = -sz; y <= sz; y++)
905 {
906 test = TileVector(t.x+x, t.y+y);
907 if (dsq->game->isObstructed(test))
908 {
909 v = true;
910 break;
911 }
912 }
913 }
914 }
915 break;
916 case OBSCHECK_4DIR:
917 {
918 for (int x = -sz; x <= sz; x++)
919 {
920 test = TileVector(t.x+x, t.y);
921 if (dsq->game->isObstructed(test))
922 {
923 v = true;
924 break;
925 }
926 }
927 for (int y = -sz; y <= sz; y++)
928 {
929 test = TileVector(t.x, t.y+y);
930 if (dsq->game->isObstructed(test))
931 {
932 v = true;
933 break;
934 }
935 }
936 }
937 break;
938 case OBSCHECK_DOWN:
939 {
940 for (int y = 0; y <= sz; y++)
941 {
942 test = TileVector(t.x, t.y+y);
943 if (dsq->game->isObstructed(test))
944 {
945 v = true;
946 break;
947 }
948 }
949 }
950 break;
951 case OBSCHECK_8DIR:
952 {
953 //debugLog("8dir");
954 for (int d = 0; d <= sz; d++)
955 {
956
957 test = TileVector(t.x+d, t.y);
958 if (dsq->game->isObstructed(test))
959 {
960 v = true;
961 break;
962 }
963 test = TileVector(t.x-d, t.y);
964 if (dsq->game->isObstructed(test))
965 {
966 v = true;
967 break;
968 }
969
970 test = TileVector(t.x, t.y+d);
971 if (dsq->game->isObstructed(test))
972 {
973 v = true;
974 break;
975 }
976 test = TileVector(t.x, t.y-d);
977 if (dsq->game->isObstructed(test))
978 {
979 v = true;
980 break;
981 }
982
983 test = TileVector(t.x-d, t.y-d);
984 if (dsq->game->isObstructed(test))
985 {
986 v = true;
987 break;
988 }
989 test = TileVector(t.x-d, t.y+d);
990 if (dsq->game->isObstructed(test))
991 {
992 v = true;
993 break;
994 }
995
996 test = TileVector(t.x+d, t.y-d);
997 if (dsq->game->isObstructed(test))
998 {
999 v = true;
1000 break;
1001 }
1002 test = TileVector(t.x+d, t.y+d);
1003 if (dsq->game->isObstructed(test))
1004 {
1005 v = true;
1006 break;
1007 }
1008 }
1009 }
1010 break;
1011 }
1012 if (hitTile)
1013 *hitTile = test;
1014 return v;
1015 }
1016
touchAvatarDamage(int radius,float dmg,const Vector & override,float speed,float pushTime,Vector collidePos)1017 bool Entity::touchAvatarDamage(int radius, float dmg, const Vector &override, float speed, float pushTime, Vector collidePos)
1018 {
1019 if (isv(EV_BEASTBURST, 1) && isDamageTarget(DT_AVATAR_BITE) && dsq->continuity.form == FORM_BEAST && dsq->game->avatar->bursting)
1020 {
1021 return false;
1022 }
1023 // this whole function has to be rewritten
1024 Vector usePosition = position;
1025 if (override.x != -1)
1026 {
1027 usePosition = override;
1028 }
1029 if (!collidePos.isZero())
1030 {
1031 usePosition = getWorldCollidePosition(collidePos);
1032
1033 /*
1034 std::ostringstream os;
1035 os << "position(" << position.x << ", " << position.y << ") - ";
1036 os << "usePosition(" << usePosition.x << ", " << usePosition.y << ") - ";
1037 os << "collidePos(" << collidePos.x << ", " << collidePos.y << ")";
1038 debugLog(os.str());
1039 */
1040
1041 }
1042 if (radius == 0 || (dsq->game->avatar->getWorldPosition() - usePosition).getSquaredLength2D() < sqr(radius+dsq->game->avatar->collideRadius))
1043 {
1044 if (dmg > 0)
1045 {
1046 DamageData d;
1047 d.damage = dmg;
1048 d.attacker = this;
1049 dsq->game->avatar->damage(d);
1050 }
1051 if (pushTime > 0 && speed > 0)
1052 {
1053 Vector diff = dsq->game->avatar->position - position;
1054 diff.setLength2D(speed);
1055 //dsq->game->avatar->vel += diff;
1056 //dsq->game->avatar->push(v, pushTime, , 0);
1057 dsq->game->avatar->push(diff, pushTime, speed, dmg);
1058 }
1059 else if (speed > 0)
1060 {
1061 dsq->game->avatar->fallOffWall();
1062 Vector diff = dsq->game->avatar->position - position;
1063 diff.setLength2D(speed);
1064 dsq->game->avatar->vel += diff;
1065 }
1066 /*
1067 if (pushTime != 0)
1068 {
1069 Vector v = (dsq->game->avatar->position - this->position);
1070 v.setLength2D(1000);
1071 dsq->game->avatar->push(v, pushTime, 800, 0);
1072 }
1073 */
1074 //dsq->game->avatar->damage(dmg);
1075 return true;
1076 }
1077 return false;
1078 }
1079
1080 const float sct = 0.15;
1081 //const float blurMax = 0.04;
1082 const float blurMax = 0.01;
1083 const float blurMin = 0.0;
1084
1085 /*
1086 const float blurMax = 0.05;
1087 const float blurMin = 0.0;
1088 */
1089
onFHScale()1090 void Entity::onFHScale()
1091 {
1092 flipScale.interpolateTo(Vector(1, 1), sct);
1093 _fh = !_fh;
1094 /*
1095 copySkel.fhTo(!_fh);
1096 skeletalSprite.alpha.interpolateTo(1, 0.5);
1097 copySkel.alpha.interpolateTo(0, 0.5);
1098 */
1099 //skeletalSprite.alpha.interpolateTo(1,sct);
1100 //blurShaderAnim.interpolateTo(Vector(blurMin,0,0), sct);
1101 fhScale = 0;
1102 }
1103
onFH()1104 void Entity::onFH()
1105 {
1106 if (!beautyFlip) return;
1107 _fh = !_fh;
1108 if (!fhScale)
1109 {
1110 flipScale = Vector(1,1);
1111 /*
1112 copySkel.children = skeletalSprite.children;
1113 copySkel.scale = skeletalSprite.scale;
1114 copySkel.position = skeletalSprite.scale;
1115 copySkel.animations = skeletalSprite.animations;
1116 */
1117 //skeletalSprite.alpha.interpolateTo(0, sct*2);
1118
1119 //skeletalSprite.alpha.interpolateTo(0.5, sct);
1120
1121 //flipScale.interpolateTo(Vector(1.5, 1), sct);
1122
1123 flipScale.interpolateTo(Vector(0.6, 1), sct);
1124
1125 //blurShaderAnim = Vector(blurMin);
1126 //blurShaderAnim.interpolateTo(Vector(blurMax,0,0), sct/2);
1127
1128 fhScale = 1;
1129 }
1130 }
1131
flipToVel()1132 void Entity::flipToVel()
1133 {
1134 if ((vel.x < -5 && isfh()) || (vel.x > 5 && !isfh()))
1135 flipHorizontal();
1136 }
1137
frozenUpdate(float dt)1138 void Entity::frozenUpdate(float dt)
1139 {
1140 }
1141
update(float dt)1142 void Entity::update(float dt)
1143 {
1144 /*
1145 if (position.isnan())
1146 position = backupPos;
1147 if (vel.isnan())
1148 vel = backupVel;
1149 */
1150 /*
1151 if (entityID == 0)
1152 assignUniqueID();
1153 */
1154
1155 Vector backupPos = position;
1156 Vector backupVel = vel;
1157
1158 bool doUpdate = (updateCull < 0 || (position - core->screenCenter).isLength2DIn(updateCull));
1159 if (doUpdate && !(pauseFreeze && dsq->game->isPaused()))
1160 {
1161
1162 if (!(getEntityType() == ET_AVATAR || getEntityType() == ET_INGREDIENT))
1163 {
1164 if (spiritFreeze && dsq->game->isWorldPaused())
1165 {
1166 // possible bug here because of return
1167 return;
1168 //dt *= 0.5f;
1169 }
1170 //if (dsq->continuity.getWorldType() != WT_NORMAL)
1171 // return;
1172 }
1173
1174 //skeletalSprite.setFreeze(true);
1175
1176 if (frozenTimer == 0 || getState() == STATE_PUSH)
1177 Quad::update(dt);
1178 onAlwaysUpdate(dt);
1179
1180 // always, always update:
1181 if (damageTimer.updateCheck(dt))
1182 {
1183 multColor.stop();
1184 multColor = Vector(1,1,1);
1185 }
1186
1187 if (!multColor.isInterpolating())
1188 {
1189 multColor = Vector(1,1,1);
1190 }
1191
1192 vineDamageTimer.update(dt);
1193
1194 if (dieTimer > 0)
1195 {
1196 dieTimer -= dt;
1197 if (dieTimer <0)
1198 {
1199 dieTimer = 0;
1200 //safeKill();
1201 setLife(1);
1202 setDecayRate(1);
1203 fadeAlphaWithLife = 1;
1204 }
1205 }
1206
1207 StateMachine::onUpdate(dt);
1208
1209 if (frozenTimer > 0)
1210 {
1211 onUpdateFrozen(dt);
1212 if (bubble)
1213 bubble->position = this->position;
1214
1215 frozenTimer -= dt;
1216 if (frozenTimer <= 0)
1217 {
1218 frozenTimer = 0;
1219 popBubble();
1220 }
1221 }
1222
1223 /*
1224 skeletalSprite.setFreeze(false);
1225 skeletalSprite.update(dt);
1226 */
1227
1228 /*
1229 std::string bgAction;
1230 if (frozenTimer > 0)
1231 {
1232 bgAction = currentAction;
1233 currentAction = "frozen";
1234 }
1235 */
1236
1237 /*
1238 if (!bgAction.empty())
1239 {
1240 currentAction = bgAction;
1241 }
1242 */
1243 }
1244
1245 updateBoneLock();
1246
1247 if (position.isNan())
1248 position = backupPos;
1249 if (vel.isNan())
1250 vel = backupVel;
1251
1252 updateSoundPosition();
1253 }
1254
postUpdate(float dt)1255 void Entity::postUpdate(float dt)
1256 {
1257 updateBoneLock();
1258 }
1259
isAvatarAttackTarget()1260 bool Entity::isAvatarAttackTarget()
1261 {
1262 return getEntityType() == ET_ENEMY && canBeTargetedByAvatar;
1263 }
1264
pathBurst(bool wallJump)1265 bool Entity::pathBurst(bool wallJump)
1266 {
1267 if (skeletalSprite.isLoaded() && (wallJump || (!wallJump && !burstTimer.isActive())))
1268 {
1269 dsq->game->playBurstSound(wallJump);
1270 skeletalSprite.animate("burst");
1271 position.ensureData();
1272 if (wallJump)
1273 position.data->pathTimeMultiplier = 2;
1274 else
1275 position.data->pathTimeMultiplier = 1.5;
1276 burstTimer.start(1);
1277 //void pathBurst();r
1278 return true;
1279 }
1280 return false;
1281 }
1282
isFollowingPath()1283 bool Entity::isFollowingPath()
1284 {
1285 return position.isFollowingPath();
1286 }
1287
onPathEnd()1288 void Entity::onPathEnd()
1289 {
1290 if (deleteOnPathEnd)
1291 {
1292 safeKill();
1293 }
1294 else
1295 {
1296 if (swimPath)
1297 {
1298 offset.interpolateTo(Vector(0, 0), 0.4);
1299 rotateToVec(Vector(0,-1), 0.1, 0);
1300 if (skeletalSprite.isLoaded())
1301 {
1302 skeletalSprite.animate("idle", -1);
1303 }
1304 position.ensureData();
1305 int num = position.data->path.getNumPathNodes();
1306 if (num >= 2)
1307 {
1308 Vector v2 = position.data->path.getPathNode(num-1)->value;
1309 Vector v1 = position.data->path.getPathNode(num-2)->value;
1310 Vector v = v2 - v1;
1311
1312 if (isv(EV_FLIPTOPATH, 1))
1313 {
1314 if (v.x > 0)
1315 {
1316 if (!isfh())
1317 flipHorizontal();
1318 }
1319 if (v.x < 0)
1320 {
1321 if (isfh())
1322 flipHorizontal();
1323 }
1324 }
1325 }
1326 }
1327 }
1328 }
1329
movementDetails(Vector v)1330 void Entity::movementDetails(Vector v)
1331 {
1332 v.normalize2D();
1333
1334 if (isv(EV_FLIPTOPATH, 1))
1335 {
1336 if (v.x < 0)
1337 {
1338 if (isfh())
1339 flipHorizontal();
1340 }
1341 else if (v.x > 0)
1342 {
1343 if (!isfh())
1344 flipHorizontal();
1345 }
1346 }
1347 // offset.interpolateTo(sinf(ondulateTimer)*v.getPerpendicularLeft()*32, 0.5);
1348 if (skeletalSprite.isLoaded())
1349 {
1350 if (burstTimer.isActive())
1351 rotateToVec(v, 0.05);
1352 else
1353 rotateToVec(v, 0.2);
1354 Animation *anim = skeletalSprite.getCurrentAnimation();
1355 if (!burstTimer.isActive())
1356 {
1357 if (!anim || anim->name != "swim")
1358 skeletalSprite.transitionAnimate("swim", 0.1, -1);
1359 }
1360 }
1361 }
1362
slowToStopPath(float t)1363 void Entity::slowToStopPath(float t)
1364 {
1365 slowingToStopPath = t;
1366 std::ostringstream os;
1367 os << "slowingToStopPath: " << slowingToStopPath;
1368 debugLog(os.str());
1369 slowingToStopPathTimer = 0;
1370 }
1371
isSlowingToStopPath()1372 bool Entity::isSlowingToStopPath()
1373 {
1374 bool v = (slowingToStopPath > 0);
1375 /*
1376 if (v)
1377 debugLog("isSlowingToStopPath: true");
1378 */
1379 return v;
1380 }
1381
1382 /*
1383 void Entity::updateAvatarRollPull(float dt)
1384 {
1385 if (dsq->game->avatar->isRolling())
1386 {
1387 if (position - dsq->game->avatar->position)
1388 {
1389 }
1390 }
1391 }
1392 */
1393
updateCurrents(float dt)1394 bool Entity::updateCurrents(float dt)
1395 {
1396 inCurrent = false;
1397 int c = 0;
1398 Vector accum;
1399
1400 //if (isUnderWater())
1401 // why?
1402 {
1403 //Path *p = dsq->game->getNearestPath(position, PATH_CURRENT);
1404 if (!dsq->game->isWorldPaused())
1405 {
1406 for (Path *p = dsq->game->getFirstPathOfType(PATH_CURRENT); p; p = p->nextOfType)
1407 {
1408 if (p->active)
1409 {
1410 for (int n = 1; n < p->nodes.size(); n++)
1411 {
1412 PathNode *node2 = &p->nodes[n];
1413 PathNode *node1 = &p->nodes[n-1];
1414 Vector dir = node2->position - node1->position;
1415 if (isTouchingLine(node1->position, node2->position, position, collideRadius + p->rect.getWidth()/2))
1416 {
1417 inCurrent = true;
1418
1419 dir.setLength2D(p->currentMod);
1420 accum += dir;
1421 c++;
1422
1423 /*
1424 dir.setLength2D(p->currentMod);
1425 vel2 += dir*dt;
1426 vel2.capLength2D(p->currentMod);
1427 */
1428 }
1429 }
1430 }
1431 }
1432 }
1433 if (inCurrent)
1434 {
1435 accum /= c;
1436 vel2 = accum;
1437 float len = vel2.getLength2D();
1438 float useLen = len;
1439 if (useLen < 500)
1440 useLen = 500;
1441 if (!(this->getEntityType() == ET_AVATAR && dsq->game->avatar->canSwimAgainstCurrents() && dsq->game->avatar->bursting))
1442 {
1443 doCollisionAvoidance(1, 4, 1, &vel2, useLen);
1444 }
1445
1446 doCollisionAvoidance(dt, 3, 1, 0, useLen);
1447
1448 Vector dist = -vel2;
1449 dist.normalize2D();
1450 float v = dist.x;
1451 float scale = 0.2;
1452 if (getEntityType() == ET_AVATAR)
1453 {
1454 if (v < 0)
1455 dsq->rumble((-v)*scale, (1.0f+v)*scale, 0.2);
1456 else
1457 dsq->rumble((1.0f-v)*scale, (v)*scale, 0.1);
1458 }
1459 }
1460 }
1461 if (this->getEntityType() == ET_AVATAR && dsq->game->avatar->canSwimAgainstCurrents())
1462 {
1463 int cap = 100;
1464 if (!vel.isZero())
1465 {
1466 if (vel.dot2D(vel2) < 0 ) // greater than 90 degrees
1467 {
1468 // against current
1469 if (dsq->game->avatar->bursting)
1470 vel2 = 0;
1471 else if (dsq->game->avatar->isSwimming())
1472 vel2.capLength2D(cap);
1473 }
1474 }
1475 else
1476 {
1477 //vel2.capLength2D(cap);
1478 }
1479 }
1480 /*
1481 if (!inCurrent && !vel2.isZero() && !vel2.isInterpolating())
1482 {
1483 vel2.interpolateTo(Vector(0,0,0), 1);
1484 }
1485 */
1486 return inCurrent;
1487 }
1488
1489 const float minVel2Len = 10;
1490
updateVel2(float dt,bool override)1491 void Entity::updateVel2(float dt, bool override)
1492 {
1493 if ((override || !inCurrent) && !vel2.isZero())
1494 {
1495 bool wasUnder = vel.isLength2DIn(minVel2Len);
1496 // PATCH_KEY: vel2 doesn't build up if dt is low
1497 // 1000 * dt
1498 Vector d = vel2;
1499 d.setLength2D(1000*dt);
1500 bool xg = (vel2.x > 0), yg = (vel2.y > 0);
1501 vel2 -= d;
1502
1503 // PATCH_KEY: could this be what breaks it? never gets out of 10?
1504 //|| vel2.isLength2DIn(10)
1505 if ((xg != (vel2.x > 0)) || (yg != (vel2.y >0)) || (!wasUnder && vel2.isLength2DIn(minVel2Len)))
1506 vel2 = 0;
1507 }
1508 }
1509
isSittingOnInvisibleIn()1510 bool Entity::isSittingOnInvisibleIn()
1511 {
1512 TileVector t(position);
1513 for (int x = 0; x < 4; x++)
1514 {
1515 for (int y = 0; y < 4; y++)
1516 {
1517 if (dsq->game->isObstructed(TileVector(t.x+x, t.y+y), OT_INVISIBLEIN))
1518 return true;
1519 if (dsq->game->isObstructed(TileVector(t.x-x, t.y+y), OT_INVISIBLEIN))
1520 return true;
1521 if (dsq->game->isObstructed(TileVector(t.x+x, t.y-y), OT_INVISIBLEIN))
1522 return true;
1523 if (dsq->game->isObstructed(TileVector(t.x-x, t.y-y), OT_INVISIBLEIN))
1524 return true;
1525 }
1526 }
1527
1528 return false;
1529
1530 //bool invisibleIn = false;
1531 ////Vector f = getForward();
1532 //Vector f = getNormal();
1533 //TileVector t(position);
1534 //float tx = float(t.x);
1535 //float ty = float(t.y);
1536 //float otx = float(t.x);
1537 //float oty = float(t.y);
1538 //
1539 //for (int check = 0; check < 4; check++)
1540 //{
1541 // /*
1542 // std::ostringstream os;
1543 // os << "check: " << check << " p(" << position.x <<", " << position.y << ") f(" << f.x << ", " << f.y << ") t(" << tx << ", " << ty << ") tile found: " << dsq->game->getGrid(TileVector(tx, ty));
1544 // debugLog(os.str());
1545 // */
1546 // if (dsq->game->getGrid(TileVector(tx, ty))==OT_EMPTY)
1547 // {
1548 // }
1549 // else if (dsq->game->getGrid(TileVector(tx, ty))==OT_INVISIBLEIN)
1550 // {
1551 // invisibleIn = true;
1552 // //debugLog("invisible in!");
1553 // break;
1554 // }
1555 // else
1556 // {
1557 // //debugLog("obstruction, aborting");
1558 // break;
1559 // }
1560 // tx -= f.x;
1561 // ty -= f.y;
1562
1563 // /*
1564 // tx = float(t.x) - f.x*float(check);
1565 // ty = float(t.y) - f.y*float(check);
1566 // */
1567 //}
1568 //return invisibleIn;
1569 }
1570
moveOutOfWall()1571 void Entity::moveOutOfWall()
1572 {
1573 /*
1574 Vector v = getWorldCollidePosition(Vector(0,-1));
1575 // HACK: is normalize necessary here? (distance of 1 presumabley)
1576 Vector n = (v - position);
1577 n.normalize2D();
1578 */
1579 Vector n = getNormal();
1580 TileVector t(position);
1581 int c = 0;
1582 bool useValue = true;
1583 while (dsq->game->isObstructed(t))
1584 {
1585 c++;
1586 if (c > 6)
1587 {
1588 //debugLog("entity: " + name + " exceeded max moveOutOfWall()");
1589 useValue = false;
1590 break;
1591 }
1592 t.x += n.x;
1593 t.y += n.y;
1594 }
1595 if (useValue)
1596 {
1597 position = t.worldVector();
1598 }
1599 }
1600
clearDamageTargets()1601 void Entity::clearDamageTargets()
1602 {
1603 disabledDamageTypes.clear();
1604 }
1605
setDamageTarget(DamageType dt,bool v)1606 void Entity::setDamageTarget(DamageType dt, bool v)
1607 {
1608 if (v)
1609 disabledDamageTypes.erase(dt);
1610 else
1611 disabledDamageTypes.insert(dt);
1612 }
1613
setEatType(EatType et,const std::string & file)1614 void Entity::setEatType(EatType et, const std::string &file)
1615 {
1616 eatType = et;
1617 if (eatType == EAT_FILE)
1618 {
1619 EatData *e = dsq->continuity.getEatData(file);
1620 if (e)
1621 eatData = *e;
1622 }
1623 }
1624
setAllDamageTargets(bool v)1625 void Entity::setAllDamageTargets(bool v)
1626 {
1627 if (v)
1628 clearDamageTargets(); // clear all disabled -> all allowed now
1629 else
1630 {
1631 for (int i = DT_ENEMY; i < DT_ENEMY_REALMAX; i++)
1632 disabledDamageTypes.insert(DamageType(i));
1633 for (int i = DT_AVATAR; i < DT_AVATAR_REALMAX; i++)
1634 disabledDamageTypes.insert(DamageType(i));
1635 for (int i = DT_AVATAR_MAX; i < DT_REALMAX; i++)
1636 disabledDamageTypes.insert(DamageType(i));
1637 }
1638 }
1639
isDamageTarget(DamageType dt)1640 bool Entity::isDamageTarget(DamageType dt)
1641 {
1642 return disabledDamageTypes.find(dt) == disabledDamageTypes.end();
1643 }
1644
getHealthPerc()1645 float Entity::getHealthPerc()
1646 {
1647 return float(health) / float(maxHealth);
1648 }
1649
getMoveVel()1650 Vector Entity::getMoveVel()
1651 {
1652 return vel + vel2;
1653 }
1654
onEndOfLife()1655 void Entity::onEndOfLife()
1656 {
1657 if (!calledEntityDied)
1658 {
1659 dsq->game->entityDied(this);
1660 calledEntityDied = true;
1661 }
1662 }
1663
setPoison(float m,float t)1664 void Entity::setPoison(float m, float t)
1665 {
1666 poison = m;
1667 poisonTimer.start(t);
1668 if (poison)
1669 poisonBitTimer.start(dsq->continuity.poisonBitTime);
1670 }
1671
onUpdate(float dt)1672 void Entity::onUpdate(float dt)
1673 {
1674 if (isv(EV_CLAMPING, 1))
1675 {
1676 if (!offset.isInterpolating())
1677 {
1678 setv(EV_CLAMPING, 0);
1679 }
1680 }
1681 vel2.update(dt);
1682
1683 if (boneLockDelay > 0)
1684 {
1685 boneLockDelay -= dt;
1686 if (boneLockDelay < 0)
1687 boneLockDelay = 0;
1688 }
1689
1690
1691 if (beautyFlip)
1692 {
1693 flipScale.update(dt);
1694
1695 switch (fhScale)
1696 {
1697 case 1:
1698 if (!flipScale.isInterpolating())
1699 onFHScale();
1700 break;
1701 }
1702
1703 //blurShaderAnim.update(dt);
1704 }
1705
1706
1707
1708 //vel2=0;
1709 Vector lastPos = position;
1710
1711 if (ridingOnEntity)
1712 {
1713 position = ridingOnEntity->position + ridingOnEntityOffset;
1714 }
1715
1716 if (hair)
1717 {
1718 hair->color.x = color.x * multColor.x;
1719 hair->color.y = color.y * multColor.y;
1720 hair->color.z = color.z * multColor.z;
1721 //hair->color = this->color;
1722 }
1723
1724 if (slowingToStopPath > 0)
1725 {
1726 /*
1727 std::ostringstream os;
1728 os << "slowingToStopPath: " << slowingToStopPath;
1729 debugLog(os.str());
1730 */
1731
1732 slowingToStopPathTimer += dt;
1733 position.ensureData();
1734 if (slowingToStopPathTimer >= slowingToStopPath)
1735 {
1736 // done
1737 position.data->pathTimeMultiplier = 1;
1738 // stopFollowingPath();
1739 idle();
1740 slowingToStopPath = 0;
1741 slowingToStopPathTimer = 0;
1742 }
1743 else
1744 {
1745 position.data->pathTimeMultiplier = 1.0f - (slowingToStopPathTimer / slowingToStopPath);
1746 }
1747 }
1748
1749 maxSpeedLerp.update(dt);
1750 velocity.z = 0;
1751 vel.z = 0;
1752
1753 bool wasFollowing = false;
1754 if (isFollowingPath())
1755 wasFollowing = true;
1756
1757 if (burstTimer.updateCheck(dt))
1758 {
1759 position.ensureData();
1760 position.data->pathTimeMultiplier = 1;
1761 }
1762
1763 if (poisonTimer.updateCheck(dt))
1764 {
1765 poison = 0;
1766 }
1767
1768 if (currentState != STATE_DEATHSCENE && currentState != STATE_DEAD)
1769 {
1770 if (poison)
1771 {
1772 if (poisonBitTimer.updateCheck(dt))
1773 {
1774 poisonBitTimer.start(dsq->continuity.poisonBitTime);
1775
1776 DamageData d;
1777 d.damageType = DT_ENEMY_ACTIVEPOISON;
1778 d.useTimer = false;
1779 d.damage = 0.5f*poison;
1780 damage(d);
1781
1782 dsq->spawnParticleEffect("PoisonBubbles", position);
1783 }
1784 }
1785 }
1786
1787 Quad::onUpdate(dt);
1788
1789 Vector v = position - lastPos;
1790 lastMove = v;
1791 if (position.isFollowingPath() && swimPath)
1792 {
1793 movementDetails(v);
1794 }
1795 else
1796 {
1797 if (watchingEntity)
1798 {
1799 Vector v = position - watchingEntity->position;
1800 if (v.x < 0)
1801 {
1802 if (!isfh())
1803 flipHorizontal();
1804 }
1805 else if (v.x > 0)
1806 {
1807 if (isfh())
1808 flipHorizontal();
1809 }
1810 }
1811 }
1812
1813 if (wasFollowing && !isFollowingPath())
1814 {
1815 onPathEnd();
1816 }
1817 multColor.update(dt);
1818
1819 if (bubble)
1820 bubble->position = this->position;
1821
1822 /*
1823 if (frozenTimer > 0)
1824 {
1825 frozenTimer --;
1826 if (frozenTimer <= 0)
1827 {
1828 frozenTimer = 0;
1829 popBubble();
1830 }
1831 }
1832 */
1833
1834 if (getState() == STATE_PUSH)
1835 {
1836 //vel = pushVec * this->time;
1837 vel = pushVec;
1838 }
1839 /*
1840 else if (this->currentAction == "freeze")
1841 {
1842 if (this->enqueuedAction != "freeze")
1843 {
1844 this->enqueuedAction = "";
1845 }
1846 if (canBeFrozen)
1847 vel = Vector(0,0,0);
1848 }
1849 */
1850 else if (followEntity)
1851 {
1852 Vector lastPos = position;
1853 Vector off;
1854 int sz = 96;
1855 if (followEntity->vel.getSquaredLength2D() > sqr(1))
1856 {
1857 off = followEntity->vel.getPerpendicularLeft();
1858 switch (followPos)
1859 {
1860 case 0: off.setLength2D(sz); break;
1861 case 1: off.setLength2D(-sz); break;
1862 }
1863 }
1864 else if (followEntity->lastMove.getSquaredLength2D() > sqr(1))
1865 {
1866
1867 off = followEntity->lastMove.getPerpendicularLeft();
1868 switch (followPos)
1869 {
1870 case 0: off.setLength2D(sz); break;
1871 case 1: off.setLength2D(-sz); break;
1872 }
1873 }
1874 Vector mov = followEntity->position + off - this->position;
1875 if (mov.getSquaredLength2D() > sqr(96))
1876 {
1877 //following = true;
1878 int spd = mov.getLength2D();
1879 spd -= 64;
1880 if (spd < 0)
1881 spd = 0;
1882 else if (spd < 400)
1883 spd *= 2;
1884 //spd /= ;
1885 else
1886 spd = 800;
1887
1888 mov.setLength2D(spd);
1889 position += mov * dt;
1890 Vector diff = position - lastPos;
1891 movementDetails(diff);
1892 }
1893 else
1894 {
1895 Animation *anim = skeletalSprite.getCurrentAnimation();
1896 if (!anim || anim->name != "idle")
1897 idle();
1898 }
1899 }
1900
1901 if (stickToNaijasHead)
1902 {
1903 position = dsq->game->avatar->headPosition;
1904 /*
1905 std::ostringstream os;
1906 os << "pos(" << dsq->game->avatar->headPosition.x << ", " <<dsq->game->avatar->headPosition.y << ")";
1907 debugLog(os.str());
1908 */
1909 }
1910
1911 updateLance(dt);
1912 }
1913
updateBoneLock()1914 void Entity::updateBoneLock()
1915 {
1916 if (boneLock.on)
1917 {
1918 /*
1919 Vector pos = boneLock.bone->getWorldCollidePosition(boneLock.localOffset);
1920 Vector bpos = boneLock.bone->getWorldPosition();
1921 position = pos;
1922 boneLock.wallNormal = pos - bpos;
1923 boneLock.wallNormal.normalize2D();
1924 rotateToVec(boneLock.wallNormal, 0.01);
1925 */
1926
1927 Vector lastPosition = position;
1928
1929 if (boneLock.bone)
1930 {
1931 position = boneLock.bone->transformedCollisionMask[boneLock.collisionMaskIndex];
1932 boneLock.wallNormal = boneLock.bone->getCollisionMaskNormal(boneLock.collisionMaskIndex);
1933 rotateToVec(boneLock.wallNormal, 0.01);
1934 }
1935 else
1936 {
1937 Vector currentOffset = getRotatedVector(boneLock.circleOffset, boneLock.entity->rotation.z - boneLock.origRot);
1938 position = boneLock.entity->getWorldPosition() + currentOffset;
1939 boneLock.wallNormal = currentOffset;
1940 boneLock.wallNormal.normalize2D();
1941 rotateToVec(boneLock.wallNormal, 0.01);
1942 }
1943
1944 if (dsq->game->collideCircleWithGrid(position, collideRadius))
1945 {
1946 position = lastPosition;
1947 setBoneLock(BoneLock());
1948 return;
1949 }
1950
1951
1952 /*
1953 Vector bpos = boneLock.bone->getWorldPosition();
1954 boneLock.wallNormal = position - bpos;
1955 rotateToVec(boneLock.wallNormal, 0.01);
1956 */
1957
1958 //debugLog("wall normal");
1959
1960
1961
1962
1963 /*
1964 Vector p = boneLock.bone->getWorldPosition();
1965 Vector o = boneLock.localOffset;
1966 position = p+o;
1967
1968 boneLock.wallNormal = o;
1969 boneLock.wallNormal.normalize2D();
1970 */
1971
1972
1973
1974 onUpdateBoneLock();
1975 /*
1976 wallNormal = o;
1977 wallNormal.normalize2D();
1978 rotateToVec(wallNormal, 0);
1979 */
1980 //position = boneLock.bone->getWorldPosition() + boneLock.localOffset;
1981 //rotation = boneLock.bone->getWorldRotation();
1982 }
1983 }
1984
getIdleAnimName()1985 std::string Entity::getIdleAnimName()
1986 {
1987 return "idle";
1988 }
1989
idle()1990 void Entity::idle()
1991 {
1992 if (isFollowingPath())
1993 stopFollowingPath();
1994 position.stopPath();
1995 perform(Entity::STATE_IDLE);
1996 skeletalSprite.stopAllAnimations();
1997 //skeletalSprite.animate("idle", -1, 0);
1998 onIdle();
1999
2000 skeletalSprite.transitionAnimate(getIdleAnimName(), 0.3, -1);
2001 rotateToVec(Vector(0,-1),0.1);
2002 vel.capLength2D(50);
2003
2004 setRiding(0);
2005 }
2006
updateLance(float dt)2007 void Entity::updateLance(float dt)
2008 {
2009 if (lance == 1)
2010 {
2011 lanceTimer -= dt;
2012 if (lanceTimer < 0)
2013 {
2014 lance = 0;
2015 lanceGfx->setLife(1.0);
2016 lanceGfx->setDecayRate(2);
2017 lanceGfx->fadeAlphaWithLife = 1;
2018 lanceGfx = 0;
2019 lanceTimer = 0;
2020 }
2021 else
2022 {
2023 lanceGfx->fhTo(_fh);
2024 lanceDelay = lanceDelay + dt;
2025 if (lanceDelay > 0.1f)
2026 {
2027 lanceDelay = 0;
2028 dsq->game->fireShot("Lance", this, 0, lanceGfx->getWorldCollidePosition(Vector(-64, 0)));
2029 }
2030
2031 if (lanceBone != 0)
2032 {
2033 lanceGfx->position = lanceBone->getWorldPosition();
2034 lanceGfx->rotation = lanceBone->getWorldRotation();
2035 }
2036 else
2037 {
2038 lanceGfx->position = getWorldPosition();
2039 lanceGfx->rotation = rotation;
2040 }
2041
2042 }
2043 }
2044 }
2045
attachLance()2046 void Entity::attachLance()
2047 {
2048 std::ostringstream os;
2049 os << "attaching lance to " << this->name;
2050 debugLog(os.str());
2051 lance = 1;
2052 lanceBone = 0;
2053 if (!lanceGfx)
2054 {
2055 lanceGfx = new PauseQuad();
2056 lanceGfx->setTexture("Particles/Lance");
2057 lanceGfx->alpha = 0;
2058 lanceGfx->alpha.interpolateTo(1, 0.5);
2059 dsq->game->addRenderObject(lanceGfx, LR_PARTICLES);
2060 }
2061 lanceTimer = 8;
2062 lanceBone = skeletalSprite.getBoneByName("Lance");
2063 }
2064
setRiding(Entity * e)2065 void Entity::setRiding(Entity *e)
2066 {
2067 riding = e;
2068 }
2069
getRiding()2070 Entity* Entity::getRiding()
2071 {
2072 return riding;
2073 }
2074
attachEntity(Entity * e,Vector offset)2075 void Entity::attachEntity(Entity *e, Vector offset)
2076 {
2077 attachedEntities.push_back(e);
2078 //e->position - position
2079 attachedEntitiesOffsets.push_back(offset);
2080 e->attachedTo = this;
2081 //dsq->game->avatar->position - avatarOffset;
2082 }
2083
detachEntity(Entity * e)2084 void Entity::detachEntity(Entity *e)
2085 {
2086 e->attachedTo = 0;
2087 std::vector<Entity*>copyEnts = attachedEntities;
2088 std::vector<Vector> copyOffs = attachedEntitiesOffsets;
2089
2090 attachedEntities.clear();
2091 attachedEntitiesOffsets.clear();
2092
2093 for (int i = 0; i < copyEnts.size(); i++)
2094 {
2095 if (copyEnts[i] != e)
2096 {
2097 attachedEntities.push_back(copyEnts[i]);
2098 attachedEntitiesOffsets.push_back(copyOffs[i]);
2099 }
2100 }
2101 }
2102
2103 //if (fabsf(rotation.z - angle) > 180)
2104 //{
2105 // rotation.z += 360;
2106 //}
2107 /*
2108 if (rotation.z > 270 && angle > -45 && angle < 0)
2109 angle = 360 + angle;
2110 */
2111
rotateToVec(Vector addVec,float time,float offsetAngle)2112 void Entity::rotateToVec(Vector addVec, float time, float offsetAngle)
2113 {
2114 // HACK: this mucks up wall normals for some reason
2115 // if (vel.getSquaredLength2D() <= 0) return;
2116 if (addVec.x == 0 && addVec.y == 0)
2117 {
2118 rotation.interpolateTo(Vector(0,0,0), time, 0);
2119 }
2120 else
2121 {
2122 float angle = MathFunctions::getAngleToVector(addVec, offsetAngle);
2123
2124 if (rotation.z <= -90 && angle >= 90)
2125 {
2126 rotation.z = 360 + rotation.z;
2127 }
2128 if (rotation.z >= 90 && angle <= -90)
2129 rotation.z = rotation.z - 360;
2130
2131
2132 /*
2133 if (rotation.z >= 270 && angle < 90)
2134 {
2135 rotation.stop();
2136 rotation.z -= 360;
2137 }
2138 if (rotation.z <= 90 && angle > 270)
2139 {
2140 rotation.stop();
2141 rotation.z += 360;
2142 }
2143 */
2144
2145 /*
2146 if (fabsf(angle - rotation.z) > 180)
2147 {
2148 // something's wrong
2149 rotation.z += 360;
2150 }
2151 */
2152
2153
2154 if (time == 0)
2155 rotation = Vector(0,0,angle);
2156 else
2157 rotation.interpolateTo(Vector(0,0,angle), time, 0);
2158 }
2159 }
2160
clearTargetPoints()2161 void Entity::clearTargetPoints()
2162 {
2163 targetPoints.clear();
2164 }
2165
addTargetPoint(const Vector & point)2166 void Entity::addTargetPoint(const Vector &point)
2167 {
2168 targetPoints.push_back(point);
2169 }
2170
getNumTargetPoints()2171 int Entity::getNumTargetPoints()
2172 {
2173 return targetPoints.size();
2174 }
2175
getTargetPoint(int i)2176 Vector Entity::getTargetPoint(int i)
2177 {
2178 if (i >= targetPoints.size() || i < 0)
2179 return getEnergyShotTargetPosition();
2180 return targetPoints[i];
2181 }
2182
getRandomTargetPoint()2183 int Entity::getRandomTargetPoint()
2184 {
2185 if (targetPoints.empty())
2186 return 0;
2187 return rand()%targetPoints.size();
2188 }
2189
isUnderWater(const Vector & override)2190 bool Entity::isUnderWater(const Vector &override)
2191 {
2192 Vector check = position;
2193 if (!override.isZero())
2194 check = override;
2195
2196 if (dsq->game->useWaterLevel && dsq->game->waterLevel.x > 0 && check.y-collideRadius > dsq->game->waterLevel.x)
2197 return true;
2198
2199
2200 Path *p = dsq->game->getNearestPath(position, PATH_WATERBUBBLE);
2201 if (p && p->active && p->isCoordinateInside(position, collideRadius))
2202 {
2203 waterBubble = p;
2204 return true;
2205 }
2206
2207 if (!dsq->game->useWaterLevel || dsq->game->waterLevel.x == 0) return true;
2208 else
2209 {
2210 if (check.y-collideRadius > dsq->game->waterLevel.x)
2211 {
2212 waterBubble = 0;
2213 return true;
2214 }
2215 }
2216
2217 return false;
2218 }
2219
push(const Vector & vec,float time,float maxSpeed,float dmg)2220 void Entity::push(const Vector &vec, float time, float maxSpeed, float dmg)
2221 {
2222 if (!this->isEntityDead())
2223 {
2224 pushDamage = dmg;
2225 if (maxSpeed == 0)
2226 {
2227 maxSpeed = this->maxSpeed;
2228 }
2229 this->pushMaxSpeed = maxSpeed;
2230 /*
2231 Vector v = vec;
2232 v.setLength2D(maxSpeed);
2233 */
2234 setState(STATE_PUSH, time);
2235 pushVec = vec;
2236 pushVec.z = 0;
2237 }
2238 //vel += pushVec;
2239 }
2240
setMaxSpeed(float ms)2241 void Entity::setMaxSpeed(float ms)
2242 {
2243 maxSpeed = ms;
2244 }
2245
getMaxSpeed()2246 int Entity::getMaxSpeed()
2247 {
2248 return maxSpeed;
2249 }
2250
songNote(int note)2251 void Entity::songNote(int note)
2252 {
2253 }
2254
songNoteDone(int note,float len)2255 void Entity::songNoteDone(int note, float len)
2256 {
2257 }
2258
sound(const std::string & sound,float freq,float fadeOut)2259 void Entity::sound(const std::string &sound, float freq, float fadeOut)
2260 {
2261 dsq->playPositionalSfx(sound, position, freq, fadeOut, this);
2262 updateSoundPosition();
2263 }
2264
getEnergyShotTargetPosition()2265 Vector Entity::getEnergyShotTargetPosition()
2266 {
2267 return getWorldPosition();
2268 }
2269
isTargetInRange(int range,int t)2270 bool Entity::isTargetInRange(int range, int t)
2271 {
2272 if (t < 0 || t >= targets.size())
2273 {
2274 std::ostringstream os;
2275 os << "isTargetInRange: invalid target index: " << t;
2276 debugLog(os.str());
2277 return false;
2278 }
2279 if (!targets[t])
2280 {
2281 debugLog ("null target");
2282 return false;
2283 }
2284 return ((targets[t]->position - this->position).getSquaredLength2D() < sqr(range));
2285 }
2286
setEntityType(EntityType et)2287 void Entity::setEntityType(EntityType et)
2288 {
2289 entityType = et;
2290 }
2291
getEntityType()2292 EntityType Entity::getEntityType()
2293 {
2294 return entityType;
2295 }
2296
2297 /* types:
2298
2299 */
findTarget(int dist,int type,int t)2300 Entity *Entity::findTarget(int dist, int type, int t)
2301 {
2302 targets[t] = 0;
2303
2304 if (type == ET_AVATAR)
2305 {
2306 Vector d = dsq->game->avatar->position - this->position;
2307 if (d.getSquaredLength2D() < sqr(dist))
2308 {
2309 targets[t] = dsq->game->avatar;
2310 }
2311 }
2312 else
2313 {
2314 int closestDist = -1;
2315 Entity *target = 0;
2316 FOR_ENTITIES(i)
2317 {
2318 Entity *e = *i;
2319 if (e != this && e->getEntityType() == type && e->health > 0)
2320 {
2321 int d = (e->position - this->position).getSquaredLength2D();
2322 if (d < sqr(dist) && (d < closestDist || closestDist == -1))
2323 {
2324 closestDist = d;
2325 target = e;
2326 }
2327 }
2328 }
2329 if (target)
2330 {
2331 targets[t] = target;
2332 }
2333 }
2334 return targets[t];
2335 }
2336
moveTowards(Vector p,float dt,int spd)2337 void Entity::moveTowards(Vector p, float dt, int spd)
2338 {
2339 Vector d = p - this->position;
2340 d.setLength2D(spd*dt);
2341 vel += d;
2342 }
2343
moveAround(Vector p,float dt,int spd,int dir)2344 void Entity::moveAround(Vector p, float dt, int spd, int dir)
2345 {
2346 Vector d = p - this->position;
2347 if (!dir)
2348 d = Vector(-d.y, d.x);
2349 else
2350 d = Vector(d.y, -d.x);
2351 d.setLength2D(spd*dt);
2352 vel += d;
2353 }
2354
moveTowardsAngle(int angle,float dt,int spd)2355 void Entity::moveTowardsAngle(int angle, float dt, int spd)
2356 {
2357 Vector p(sinf(MathFunctions::toRadians(angle))*16+position.x, cosf(MathFunctions::toRadians(angle))*16+position.y);
2358 moveTowards(p, dt, spd);
2359 }
2360
moveAroundAngle(int angle,float dt,int spd,int dir)2361 void Entity::moveAroundAngle(int angle, float dt, int spd, int dir)
2362 {
2363 Vector p(sinf(MathFunctions::toRadians(angle))*16+position.x, cosf(MathFunctions::toRadians(angle))*16+position.y);
2364 moveAround(p, dt, spd, dir);
2365 }
2366
moveTowardsTarget(float dt,int spd,int t)2367 void Entity::moveTowardsTarget(float dt, int spd, int t)
2368 {
2369 if (!targets[t]) return;
2370 moveTowards(targets[t]->position, dt, spd);
2371 }
2372
moveAroundTarget(float dt,int spd,int dir,int t)2373 void Entity::moveAroundTarget(float dt, int spd, int dir, int t)
2374 {
2375 if (!targets[t]) return;
2376 moveAround(targets[t]->position, dt, spd, dir);
2377 }
2378
getLookAtPoint()2379 Vector Entity::getLookAtPoint()
2380 {
2381 if (lookAtPoint.isZero())
2382 return position;
2383 return lookAtPoint;
2384 }
2385
2386
moveAroundEntity(float dt,int spd,int dir,Entity * e)2387 void Entity::moveAroundEntity(float dt, int spd, int dir, Entity *e)
2388 {
2389 Vector d = e->position - this->position;
2390 if (!dir)
2391 d = Vector(-d.y, d.x);
2392 else
2393 d = Vector(d.y, -d.x);
2394 d.setLength2D(spd*dt);
2395 vel += d;
2396 }
2397
onEnterState(int action)2398 void Entity::onEnterState(int action)
2399 {
2400 switch (action)
2401 {
2402 case STATE_DEAD:
2403 {
2404 if (!isGoingToBeEaten())
2405 {
2406 if (!deathSound.empty())
2407 sound(deathSound, (800 + rand()%400)/1000.0f);
2408 }
2409 else
2410 {
2411 sound("Gulp");
2412 }
2413 popBubble();
2414 //dsq->game->avatar->entityDied(this);
2415 Shot::targetDied(this);
2416 if (!calledEntityDied)
2417 {
2418 dsq->game->entityDied(this);
2419 calledEntityDied = true;
2420 }
2421 if (hair)
2422 {
2423 hair->setLife(1);
2424 hair->setDecayRate(10);
2425 hair->fadeAlphaWithLife = 1;
2426 hair = 0;
2427 }
2428
2429 }
2430 break;
2431 case STATE_PUSH:
2432 {
2433 setMaxSpeed(this->pushMaxSpeed);
2434 }
2435 break;
2436 }
2437 }
2438
isPullable()2439 bool Entity::isPullable()
2440 {
2441 return ((isEntityProperty(EP_MOVABLE)) || (frozenTimer > 0));
2442 }
2443
freeze(float time)2444 void Entity::freeze(float time)
2445 {
2446 //HACK: prevent freeze from freezing if the enemy won't deal with it properly
2447 //if (isv(EV_MOVEMENT, 0)) return;
2448
2449 vel = 0;
2450 frozenTimer = time;
2451
2452 disableMotionBlur();
2453
2454 vel.capLength2D(100);
2455
2456 onFreeze();
2457 if (!bubble)
2458 {
2459 bubble = new Quad;
2460 bubble->setTexture("spell-bubble");
2461 bubble->position = this->position;
2462 bubble->scale = Vector(0.2,0.2);
2463 bubble->scale.interpolateTo(Vector(2,2), 0.5, 0, 0, 1);
2464 bubble->alpha.ensureData();
2465 bubble->alpha.data->path.addPathNode(0.5, 0);
2466 bubble->alpha.data->path.addPathNode(0.5, 0.75);
2467 bubble->alpha.data->path.addPathNode(0, 1);
2468 bubble->alpha.startPath(time+time*0.25f);
2469 core->getTopStateData()->addRenderObject(bubble, LR_PARTICLES);
2470
2471 }
2472 }
2473
onExitState(int action)2474 void Entity::onExitState(int action)
2475 {
2476 switch(action)
2477 {
2478 case STATE_PUSH:
2479 {
2480 setState(STATE_IDLE);
2481 }
2482 break;
2483 case STATE_PUSHDELAY:
2484 {
2485 setState(STATE_IDLE);
2486 }
2487 break;
2488 case STATE_DEATHSCENE:
2489 {
2490 setState(STATE_DEAD);
2491 }
2492 break;
2493 }
2494
2495 /*
2496 // this is bad, prevents DEATH
2497 // try to prevent exitState from changing from deathscene
2498 if (health <= 0)
2499 {
2500 enqueuedState = STATE_NONE;
2501 }
2502 */
2503 /*
2504 else if (action == "freezeRecover")
2505 {
2506 enqueuePerform("idle", -1);
2507 }
2508
2509 else if (action == "freeze")
2510 {
2511 if (nextAction == "freeze")
2512 {
2513 }
2514 else
2515 {
2516 popBubble();
2517 //enqueuePerform("idle", -1);
2518 }
2519 }
2520 */
2521 }
2522
popBubble()2523 void Entity::popBubble()
2524 {
2525 /*
2526 if (currentAction == "freeze")
2527 {
2528 sound("pop");
2529 enqueuePerform("freezeRecover",1);
2530 }
2531 */
2532
2533 if (bubble)
2534 {
2535 frozenTimer = 0;
2536 sound ("Pop");
2537 bubble->setLife(1);
2538 bubble->setDecayRate(4);
2539 bubble->fadeAlphaWithLife = bubble->alpha.x;
2540 bubble = 0;
2541 dsq->spawnParticleEffect("PopEnemyBubble", position);
2542 }
2543 }
2544
2545 /*
2546 bool Entity::onDamage(int amount, Spell *spell, Entity *attacker)
2547 {
2548 if (bubble)
2549 {
2550 popBubble();
2551 onDamage(1, spell, attacker);
2552 }
2553 return true;
2554 }
2555 */
2556
isHit()2557 bool Entity::isHit()
2558 {
2559 return (damageTimer.isActive());
2560 }
2561
isInvincible()2562 bool Entity::isInvincible()
2563 {
2564 return (invincible);
2565 //|| (invincibleBreak && damageTimer.isActive())
2566 }
2567
setInvincible(bool inv)2568 void Entity::setInvincible(bool inv)
2569 {
2570 invincible = inv;
2571 }
2572
isInDarkness()2573 bool Entity::isInDarkness()
2574 {
2575 for (Element *e = dsq->getFirstElementOnLayer(12); e; e = e->bgLayerNext)
2576 {
2577 if (e->isCoordinateInside(position))
2578 return true;
2579 }
2580 return false;
2581 }
2582
canSetState(int state)2583 bool Entity::canSetState(int state)
2584 {
2585 if (enqueuedState == STATE_DEAD || currentState == STATE_DEAD || nextState == STATE_DEAD)
2586 {
2587 std::ostringstream os;
2588 os << "entity: " << name << " tried to set state to: " << state << " when in/entering dead";
2589 debugLog(os.str());
2590
2591 return false;
2592 }
2593 else if (enqueuedState == STATE_DEATHSCENE || currentState == STATE_DEATHSCENE || nextState == STATE_DEATHSCENE)
2594 {
2595 if (state == STATE_DEAD)
2596 return true;
2597 else
2598 {
2599 std::ostringstream os;
2600 os << "entity: " << name << " tried to set state to: " << state << " when in/entering deathscene";
2601 debugLog(os.str());
2602 return false;
2603 }
2604 }
2605 return true;
2606 }
2607
updateLocalWarpAreas(bool affectAvatar)2608 bool Entity::updateLocalWarpAreas(bool affectAvatar)
2609 {
2610 for (int i = 0; i < dsq->game->getNumPaths(); i++)
2611 {
2612 Path *p = dsq->game->getPath(i);
2613 if (!p->nodes.empty())
2614 {
2615 PathNode *n = &p->nodes[0];
2616 if (p && n) // && core->getNestedMains() == 1
2617 {
2618 if (p->warpMap.empty() && !p->warpNode.empty() && p->isCoordinateInside(position))
2619 {
2620 Path *p2 = dsq->game->getPathByName(p->warpNode);
2621 if (p2)
2622 {
2623 if (affectAvatar)
2624 {
2625 // HACK: do something in the script to get the avatar position
2626 dsq->game->avatar->position = this->position;
2627
2628 dsq->game->preLocalWarp(p->localWarpType);
2629 }
2630 position = p2->getPathNode(0)->position;
2631 if (affectAvatar)
2632 {
2633 // HACK: do something in the script to get the avatar position
2634 dsq->game->avatar->position = this->position;
2635
2636 dsq->game->postLocalWarp();
2637 }
2638 return true;
2639 }
2640 }
2641 }
2642 }
2643 }
2644 return false;
2645 }
2646
warpLastPosition()2647 void Entity::warpLastPosition()
2648 {
2649 position = lastPosition;
2650 }
2651
spawnParticlesFromCollisionMask(const std::string & p,int intv)2652 void Entity::spawnParticlesFromCollisionMask(const std::string &p, int intv)
2653 {
2654 for (int i = 0; i < skeletalSprite.bones.size(); i++)
2655 {
2656 for (int j = 0; j < skeletalSprite.bones[i]->collisionMask.size(); j+=intv)
2657 {
2658 Vector pos = skeletalSprite.bones[i]->getWorldCollidePosition(skeletalSprite.bones[i]->collisionMask[j]);
2659 dsq->spawnParticleEffect(p, pos);
2660 }
2661 }
2662 }
2663
2664 //Entity *e, Entity *attacker, Bone *bone, SpellType spell, int dmg)
2665 // not sure why this returns bool
2666 // return true if did damage, else false
damage(const DamageData & dmgData)2667 bool Entity::damage(const DamageData &dmgData)
2668 {
2669 DamageData d = dmgData;
2670 if (d.damageType == DT_NONE)
2671 return false;
2672 //if () return true;
2673 if (isEntityDead())
2674 {
2675 //DUPE: same as below
2676 //HACK: hackish
2677 return false;
2678 }
2679 onDamage(d);
2680 lastDamage = d;
2681 if (invincibleBreak && damageTimer.isActive() && dmgData.useTimer) return false;
2682
2683 this->multColor = Vector(1,1,1);
2684 this->multColor.stop();
2685 /*
2686 std::ostringstream os;
2687 os << "starting damage timer: " << vars->damageTime;
2688 debugLog(os.str());
2689 */
2690 if (dmgData.useTimer)
2691 damageTimer.start(damageTime);
2692 //3
2693
2694 //DUPE: same as above
2695 //HACK: hackish
2696
2697 if (d.damageType == DT_AVATAR_BITE)
2698 {
2699 debugLog("Entity::damage bittenEntities.push_back");
2700 dsq->game->avatar->bittenEntities.push_back(this);
2701 }
2702
2703 if (d.damage > 0 && frozenTimer)
2704 {
2705 popBubble();
2706 }
2707
2708 //HACK: fish form freeze
2709 if (d.damageType == DT_AVATAR_BUBBLE && isDamageTarget(DT_AVATAR_BUBBLE))
2710 {
2711 freeze(30);
2712 }
2713
2714 if (d.damageType == DT_AVATAR_VINE)
2715 {
2716 if (vineDamageTimer.isDone())
2717 {
2718 vineDamageTimer.start(0.25);
2719 }
2720 else
2721 return false;
2722 }
2723
2724 if (d.damageType == DT_ENEMY_POISON)
2725 {
2726 if (getEntityType() != ET_AVATAR)
2727 {
2728 setPoison(1, d.effectTime);
2729 }
2730 }
2731
2732 bool doDamage = !invincible;
2733
2734 if (entityType == ET_AVATAR)
2735 doDamage = (!invincible || !dsq->game->invincibleOnNested);
2736
2737 if (doDamage)
2738 {
2739 if (d.damage>0)
2740 {
2741 if (entityType == ET_AVATAR)
2742 this->multColor.interpolateTo(Vector(1, 0.1, 0.1), 0.1, 14, 1);
2743 else
2744 this->multColor.interpolateTo(Vector(1, 0.1, 0.1), 0.1, 4, 1);
2745 }
2746
2747 health -= d.damage;
2748 if (health <= 0)
2749 {
2750 health = 0;
2751 entityDead = true;
2752 if (deathScene)
2753 setState(STATE_DEATHSCENE, 0);
2754 else
2755 setState(STATE_DEAD);
2756 }
2757
2758 onHealthChange(-d.damage);
2759 }
2760
2761 return true;
2762 }
2763
clampToHit()2764 void Entity::clampToHit()
2765 {
2766 Vector dist = dsq->game->lastCollidePosition - position;
2767 dist.setLength2D(collideRadius);
2768 position = dsq->game->lastCollidePosition + dist;
2769 setv(EV_CRAWLING, 1);
2770 //setCrawling(true);
2771 }
2772
2773 /*
2774 void Entity::damage(int amount, Spell *spell, Entity *attacker)
2775 {
2776 //if (dsq->continuity.getWorldType() != WT_NORMAL) return;
2777 if (!takeDamage) return;
2778 if (isEntityDead()) return;
2779 if (invincibleBreak && this->multColor.isInterpolating()) return;
2780 if (onDamage(amount, spell, attacker))
2781 {
2782 //color = currentColor;
2783 this->multColor.interpolateTo(Vector(1, 0.5, 0.5), 0.1, 3, 1);
2784 health -= amount;
2785 if (health <= 0)
2786 {
2787 if (attacker)
2788 attacker->getEXP(exp);
2789 health = 0;
2790 entityDead = true;
2791 perform(STATE_DEAD);
2792 }
2793 }
2794 }
2795 */
2796
doEntityAvoidance(float dt,int range,float mod,Entity * ignore)2797 void Entity::doEntityAvoidance(float dt, int range, float mod, Entity *ignore)
2798 {
2799 Vector accum;
2800 int c = 0;
2801 Vector diff;
2802 FOR_ENTITIES(i)
2803 {
2804 Entity *e = *i;
2805
2806 if (e != this && e != ignore && e->ridingOnEntity != this && !e->getv(EV_NOAVOID))
2807 {
2808 diff = (this->position - e->position);
2809 if (diff.isLength2DIn(range) && !diff.isZero())
2810 {
2811 diff.setLength2D(range - diff.getLength2D());
2812 accum += diff;
2813 c++;
2814 }
2815 }
2816 }
2817 if (accum.x != 0 || accum.y != 0)
2818 {
2819 accum /= c;
2820 accum /= range;
2821 vel += accum*getMaxSpeed()*mod;
2822 }
2823 }
2824
render()2825 void Entity::render()
2826 {
2827 InterpolatedVector bcolor = color;
2828 InterpolatedVector bscale = scale;
2829
2830 scale *= flipScale;
2831 if (multColor.isInterpolating())
2832 {
2833 color *= multColor;
2834 }
2835
2836 #ifdef AQUARIA_BUILD_SCENEEDITOR
2837 if (dsq->game->isSceneEditorActive() && dsq->game->sceneEditor.editType == ET_ENTITIES)
2838 {
2839 if (dsq->game->sceneEditor.editingEntity == this)
2840 renderBorderColor = Vector(1,1,1);
2841 else
2842 renderBorderColor = Vector(0.5,0.5,0.5);
2843 renderBorder = true;
2844 //errorLog("!^!^$");
2845 }
2846 #endif
2847
2848 // HACK: need to multiply base + etc
2849 skeletalSprite.setColorMult(this->color, this->alpha.x);
2850 /*bool set=false;
2851 if (beautyFlip && blurShader.isLoaded() && flipScale.isInterpolating() && dsq->user.video.blur)
2852 {
2853 //swizzle
2854 blurShader.setValue(color.x, color.y, color.z, blurShaderAnim.x);
2855 blurShader.bind();
2856 set = true;
2857 }*/
2858 Quad::render();
2859 //if (beautyFlip && blurShader.isLoaded() && flipScale.isInterpolating())
2860 //if (set)
2861 // blurShader.unbind();
2862 renderBorder = false;
2863 skeletalSprite.clearColorMult();
2864 color = bcolor;
2865 scale = bscale;
2866 }
2867
doGlint(const Vector & position,const Vector & scale,const std::string & tex,RenderObject::BlendTypes bt)2868 void Entity::doGlint(const Vector &position, const Vector &scale, const std::string &tex, RenderObject::BlendTypes bt)
2869 {
2870 float glintTime = 0.4;
2871 Quad *glint = new Quad;
2872 //glint->setBlendType(RenderObject::BLEND_ADD);
2873 glint->setBlendType(bt);
2874 glint->setTexture(tex);
2875 glint->scale = Vector(0.5,0.5);
2876 glint->position = position;
2877 glint->scale.interpolateTo(scale, glintTime);
2878 glint->alpha.ensureData();
2879 glint->alpha.data->path.addPathNode(1, 0);
2880 glint->alpha.data->path.addPathNode(1, 0.7);
2881 glint->alpha.data->path.addPathNode(0, 1);
2882 glint->alpha.startPath(glintTime);
2883 //glint->rotation.interpolateTo(Vector(0,0,360), glintTime);
2884 glint->rotation.z = this->rotation.z;
2885 glint->setLife(glintTime);
2886 glint->setDecayRate(1);
2887 core->getTopStateData()->addRenderObject(glint, LR_PARTICLES);
2888 }
2889
addIgnoreShotDamageType(DamageType dt)2890 void Entity::addIgnoreShotDamageType(DamageType dt)
2891 {
2892 ignoreShotDamageTypes.push_back(dt);
2893 }
2894
doSpellAvoidance(float dt,int range,float mod)2895 void Entity::doSpellAvoidance(float dt, int range, float mod)
2896 {
2897 BBGE_PROF(Entity_doSpellAvoidance);
2898 Vector accum;
2899 /*
2900 int c = 0;
2901 for (int i = 0; i < dsq->game->spells.size(); i++)
2902 {
2903 Spell *s = dsq->game->spells[i];
2904 if ((s->position - this->position).getSquaredLength2D() < sqr(range))
2905 {
2906 Vector d = this->position - s->position;
2907 d.z=0;
2908 d |= range - d.getLength2D();
2909 accum += d;
2910 c++;
2911 }
2912 }
2913 if (accum.x != 0 || accum.y != 0)
2914 {
2915 accum /= c;
2916 accum /= range;
2917 vel += accum*getMaxSpeed()*mod;
2918 }
2919 */
2920 int c = 0;
2921 for (Shot::Shots::iterator i = Shot::shots.begin(); i != Shot::shots.end(); i++)
2922 {
2923 Shot *s = (Shot*)(*i);
2924
2925 if (s->isActive() && (s->position - this->position).getSquaredLength2D() < sqr(range))
2926 {
2927 for (int j = 0; j < ignoreShotDamageTypes.size(); j++)
2928 {
2929 if (s->getDamageType() == ignoreShotDamageTypes[j])
2930 {
2931 continue;
2932 }
2933 }
2934 Vector d = this->position - s->position;
2935 if (!d.isZero())
2936 {
2937 d.setLength2D(range - d.getLength2D());
2938 }
2939 accum += d;
2940 c++;
2941 }
2942 }
2943 if (accum.x != 0 || accum.y != 0)
2944 {
2945 accum /= c;
2946 accum /= range;
2947 vel += accum*getMaxSpeed()*mod;
2948 }
2949 }
2950
fillGrid()2951 void Entity::fillGrid()
2952 {
2953 if (fillGridFromQuad)
2954 {
2955 dsq->game->fillGridFromQuad(this, OT_INVISIBLEENT);
2956 }
2957 }
2958
assignUniqueID()2959 void Entity::assignUniqueID()
2960 {
2961 int id = 1;
2962 while (1)
2963 {
2964 bool isFree = true;
2965 FOR_ENTITIES(i)
2966 {
2967 Entity *e = *i;
2968 if (e != this)
2969 {
2970 if (e->getID() == id)
2971 {
2972 isFree = false;
2973 break;
2974 }
2975 }
2976 }
2977 if (isFree)
2978 {
2979 break;
2980 }
2981 id++;
2982 }
2983 entityID = id;
2984 }
2985
setID(int id)2986 void Entity::setID(int id)
2987 {
2988 entityID = id;
2989 FOR_ENTITIES(i)
2990 {
2991 Entity *e = *i;
2992 if (e != this)
2993 {
2994 if (e->getID() == entityID)
2995 {
2996 std::ostringstream os;
2997 os << "ID conflict between " << name << " and " << e->name;
2998 debugLog(os.str());
2999 e->assignUniqueID();
3000 }
3001 }
3002 }
3003 }
3004
getID()3005 int Entity::getID()
3006 {
3007 /*
3008 for (int i = 0; i < dsq->entities.size(); i++)
3009 {
3010 if (dsq->entities[i] == this)
3011 return i+1;
3012 }
3013 return 0;
3014 */
3015 return entityID;
3016 }
3017
shotHitEntity(Entity * hit,Shot * shot,Bone * b)3018 void Entity::shotHitEntity(Entity *hit, Shot *shot, Bone *b)
3019 {
3020 }
3021
doCollisionAvoidance(float dt,int search,float mod,Vector * vp,float overrideMaxSpeed,int ignoreObs,bool onlyVP)3022 bool Entity::doCollisionAvoidance(float dt, int search, float mod, Vector *vp, float overrideMaxSpeed, int ignoreObs, bool onlyVP)
3023 {
3024 Vector accum;
3025 int c = 0;
3026 bool isInWaterBubble = false;
3027 if (waterBubble && isUnderWater())
3028 {
3029 //debugLog("collision avoidance in bubble");
3030 isInWaterBubble = true;
3031 if (!canLeaveWater)
3032 {
3033 Vector a = position - waterBubble->nodes[0].position;
3034 Vector b = a;
3035 b.setLength2D((waterBubble->rect.getWidth()*0.5f) - b.getLength2D());
3036 if (b.isLength2DIn(search*TILE_SIZE))
3037 {
3038 /*
3039 std::ostringstream os;
3040 os << "b( " << b.x << ", " << b.y << ")";
3041 debugLog(os.str());
3042 */
3043 accum -= b;
3044 c++;
3045 //vel -= accum*getMaxSpeed()*mod;
3046 //return true;
3047 }
3048 }
3049 }
3050 if (!overrideMaxSpeed)
3051 overrideMaxSpeed = getMaxSpeed();
3052 if (vp == 0)
3053 vp = &this->vel;
3054 Vector vel = *vp;
3055 int minDist=-1;
3056 TileVector t(position);
3057 TileVector useTile;
3058 const int blockObs = ~ignoreObs;
3059
3060 float totalDist = sqrtf(float(sqr((search*2)*TILE_SIZE)+sqr((search*2)*TILE_SIZE)));
3061 for (int x = -search; x <= search; x++)
3062 {
3063 for (int y = -search; y <= search; y++)
3064 {
3065 TileVector checkT(t.x+x, t.y+y);
3066 bool waterBlocked=false;
3067 if (!isInWaterBubble && !canLeaveWater && checkT.worldVector().y - collideRadius < dsq->game->getWaterLevel())
3068 {
3069 waterBlocked = true;
3070 }
3071 if (waterBlocked || dsq->game->isObstructed(checkT))
3072 {
3073 if (dsq->game->isObstructed(checkT, blockObs))
3074 {
3075 Vector vtc(t.x+x, t.y+y);
3076 Vector vt(t.x, t.y);
3077 int dist = (vt-vtc).getSquaredLength2D();
3078 if (minDist == -1 || dist<minDist)
3079 {
3080 minDist = dist;
3081 useTile = TileVector(t.x+x, t.y+y);
3082 }
3083 Vector v = position - checkT.worldVector();
3084 accum += v;
3085
3086 c++;
3087 }
3088 }
3089 }
3090 }
3091 if (c > 0)
3092 {
3093 accum /= float(c) * (totalDist/2);
3094 accum.setLength2D(1.0f - accum.getLength2D());
3095 if (onlyVP)
3096 {
3097 *vp += accum*overrideMaxSpeed*mod;
3098 if (!(*vp).isLength2DIn(overrideMaxSpeed))
3099 (*vp).capLength2D(overrideMaxSpeed);
3100 }
3101 else
3102 vel += accum*overrideMaxSpeed*mod;
3103 }
3104 if (!onlyVP)
3105 *vp = vel;
3106 if (c > 0)
3107 return true;
3108 return false;
3109 }
3110
initHair(int numSegments,float segmentLength,float width,const std::string & tex)3111 void Entity::initHair(int numSegments, float segmentLength, float width, const std::string &tex)
3112 {
3113 if (hair)
3114 {
3115 errorLog("Trying to init hair when hair is already present");
3116 }
3117 hair = new Hair(numSegments, segmentLength, width);
3118 hair->setTexture(tex);
3119 dsq->game->addRenderObject(hair, layer);
3120 }
3121
3122
setHairHeadPosition(const Vector & pos)3123 void Entity::setHairHeadPosition(const Vector &pos)
3124 {
3125 if (hair)
3126 {
3127 hair->setHeadPosition(pos);
3128 }
3129 }
3130
updateHair(float dt)3131 void Entity::updateHair(float dt)
3132 {
3133 if (hair)
3134 {
3135 hair->updatePositions();
3136 }
3137 }
3138
exertHairForce(const Vector & force,float dt)3139 void Entity::exertHairForce(const Vector &force, float dt)
3140 {
3141 if (hair)
3142 {
3143 hair->exertForce(force, dt);
3144 }
3145 }
3146
isEntityInside()3147 bool Entity::isEntityInside()
3148 {
3149 FOR_ENTITIES(i)
3150 {
3151 Entity *e = *i;
3152 if (e && e->life == 1 && e != this && e->ridingOnEntity != this && isCoordinateInside(e->position))
3153 return true;
3154
3155 }
3156 return false;
3157 }
3158
updateSoundPosition()3159 void Entity::updateSoundPosition()
3160 {
3161 SoundHolder::updateSoundPosition(position.x + offset.x, position.y + offset.y);
3162 }
3163