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(&copySkel);
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