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 "ScriptedEntity.h"
22 #include "DSQ.h"
23 #include "Game.h"
24 #include "Avatar.h"
25 #include "Shot.h"
26 
27 bool ScriptedEntity::runningActivation = false;
28 
ScriptedEntity(const std::string & scriptName,Vector position,EntityType et)29 ScriptedEntity::ScriptedEntity(const std::string &scriptName, Vector position, EntityType et) : CollideEntity(), Segmented(2, 26)
30 {
31 	addType(SCO_SCRIPTED_ENTITY);
32 	crushDelay = 0;
33 	script = 0;
34 	songNoteFunction = songNoteDoneFunction = true;
35 	addChild(&pullEmitter, PM_STATIC);
36 
37 	hair = 0;
38 	becomeSolidDelay = false;
39 	strandSpacing = 10;
40 	animKeyFunc = true;
41 	canShotHitFunc = true;
42 	//runningActivation = false;
43 
44 	setEntityType(et);
45 	myTimer = 0;
46 	layer = LR_ENTITIES;
47 	surfaceMoveDir = 1;
48 	this->position = position;
49 	numSegments = 0;
50 	reverseSegments = false;
51 	manaBallAmount = 1;
52 	this->name = scriptName;
53 
54 	std::string file;
55 	if (!scriptName.empty())
56 	{
57 		if (scriptName[0]=='@' && dsq->mod.isActive())
58 		{
59 			file = dsq->mod.getPath() + "scripts/" + scriptName.substr(1, scriptName.size()) + ".lua";
60 			this->name = scriptName.substr(1, scriptName.size());
61 		}
62 		else if (dsq->mod.isActive())
63 		{
64 			file = dsq->mod.getPath() + "scripts/" + scriptName + ".lua";
65 
66 			if (!exists(file))
67 			{
68 				file = "scripts/entities/" + scriptName + ".lua";
69 			}
70 		}
71 		else
72 		{
73 			file = "scripts/entities/" + scriptName + ".lua";
74 		}
75 	}
76 	script = dsq->scriptInterface.openScript(file);
77 	if (!script)
78 	{
79 		debugLog("Could not load script [" + file + "]");
80 	}
81 }
82 
setAutoSkeletalUpdate(bool v)83 void ScriptedEntity::setAutoSkeletalUpdate(bool v)
84 {
85 	skeletalSprite.ignoreUpdate = !v;
86 }
87 
message(const std::string & msg,int v)88 void ScriptedEntity::message(const std::string &msg, int v)
89 {
90 	if (script)
91 	{
92 		if (!script->call("msg", this, msg.c_str(), v))
93 			debugLog(name + " : msg : " + script->getLastError());
94 	}
95 	Entity::message(msg, v);
96 }
97 
messageVariadic(lua_State * L,int nparams)98 int ScriptedEntity::messageVariadic(lua_State *L, int nparams)
99 {
100 	if (script)
101 	{
102 		int res = script->callVariadic("msg", L, nparams, this);
103 		if (res < 0)
104 			luaDebugMsg("msg", script->getLastError());
105 		else
106 			return res;
107 	}
108 	return Entity::messageVariadic(L, nparams);
109 }
110 
warpSegments()111 void ScriptedEntity::warpSegments()
112 {
113 	Segmented::warpSegments(position);
114 }
115 
init()116 void ScriptedEntity::init()
117 {
118 	if (script)
119 	{
120 		if (!script->call("init", this))
121 			luaDebugMsg("init", script->getLastError());
122 	}
123 
124 	Entity::init();
125 }
126 
postInit()127 void ScriptedEntity::postInit()
128 {
129 	if (script)
130 	{
131 		if (!script->call("postInit", this))
132 			luaDebugMsg("postInit", script->getLastError());
133 	}
134 
135 	Entity::postInit();
136 }
137 
initEmitter(int emit,const std::string & file)138 void ScriptedEntity::initEmitter(int emit, const std::string &file)
139 {
140 	if (emitters.size() <= emit)
141 	{
142 		emitters.resize(emit+1);
143 	}
144 	if (emitters[emit] != 0)
145 	{
146 		errorLog("Trying to init emitter being used");
147 		return;
148 	}
149 	emitters[emit] = new ParticleEffect;
150 	addChild(emitters[emit], PM_POINTER);
151 	emitters[emit]->load(file);
152 }
153 
startEmitter(int emit)154 void ScriptedEntity::startEmitter(int emit)
155 {
156 	if(emit >= emitters.size())
157 		return;
158 
159 	if (emitters[emit])
160 	{
161 		emitters[emit]->start();
162 	}
163 }
164 
stopEmitter(int emit)165 void ScriptedEntity::stopEmitter(int emit)
166 {
167 	if(emit >= emitters.size())
168 		return;
169 
170 	if (emitters[emit])
171 	{
172 		emitters[emit]->stop();
173 	}
174 }
175 
getEmitter(int emit)176 ParticleEffect *ScriptedEntity::getEmitter(int emit)
177 {
178 	return (size_t(emit) < emitters.size()) ? emitters[emit] : NULL;
179 }
180 
getNumEmitters() const181 int ScriptedEntity::getNumEmitters() const
182 {
183 	return emitters.size();
184 }
185 
registerNewPart(RenderObject * r,const std::string & name)186 void ScriptedEntity::registerNewPart(RenderObject *r, const std::string &name)
187 {
188 	partMap[name] = r;
189 }
190 
initSegments(int numSegments,int minDist,int maxDist,std::string bodyTex,std::string tailTex,int w,int h,float taper,bool reverseSegments)191 void ScriptedEntity::initSegments(int numSegments, int minDist, int maxDist, std::string bodyTex, std::string tailTex, int w, int h, float taper, bool reverseSegments)
192 {
193 	this->reverseSegments = reverseSegments;
194 	this->numSegments = numSegments;
195 	this->minDist = minDist;
196 	this->maxDist = maxDist;
197 	segments.resize(numSegments);
198 	for (int i = segments.size()-1; i >= 0 ; i--)
199 	{
200 		Quad *q = new Quad;
201 		if (i == segments.size()-1)
202 			q->setTexture(tailTex);
203 		else
204 			q->setTexture(bodyTex);
205 		q->setWidthHeight(w, h);
206 
207 		if (i > 0 && i < segments.size()-1 && taper !=0)
208 			q->scale = Vector(1.0f-(i*taper), 1-(i*taper));
209 		dsq->game->addRenderObject(q, LR_ENTITIES);
210 		segments[i] = q;
211 	}
212 	Segmented::initSegments(position);
213 }
214 
setupEntity(const std::string & tex,int lcode)215 void ScriptedEntity::setupEntity(const std::string &tex, int lcode)
216 {
217 	setEntityType(ET_NEUTRAL);
218 	if (!tex.empty())
219 		setTexture(tex);
220 
221 	updateCull = -1;
222 	manaBallAmount = 0;
223 	setState(STATE_IDLE);
224 
225 	this->layer = dsq->getEntityLayerToLayer(lcode);
226 }
227 
setupBasicEntity(const std::string & texture,int health,int manaBall,int exp,int money,float collideRadius,int state,int w,int h,int expType,bool hitEntity,int updateCull,int layer)228 void ScriptedEntity::setupBasicEntity(const std::string& texture, int health, int manaBall, int exp, int money, float collideRadius, int state, int w, int h, int expType, bool hitEntity, int updateCull, int layer)
229 {
230 	//this->updateCull = updateCull;
231 	updateCull = -1;
232 
233 	if (texture.empty())
234 		renderQuad = false;
235 	else
236 		setTexture(texture);
237 	this->health = maxHealth = health;
238 	this->collideRadius = collideRadius;
239 	setState(state);
240 	this->manaBallAmount = manaBall;
241 	width = w;
242 	height = h;
243 
244 	setEntityLayer(layer);
245 }
246 
setEntityLayer(int lcode)247 void ScriptedEntity::setEntityLayer(int lcode)
248 {
249 	this->layer = dsq->getEntityLayerToLayer(lcode);
250 }
251 
initStrands(int num,int segs,int dist,int strandSpacing,Vector color)252 void ScriptedEntity::initStrands(int num, int segs, int dist, int strandSpacing, Vector color)
253 {
254 	this->strandSpacing = strandSpacing;
255 	strands.resize(num);
256 	for (int i = 0; i < strands.size(); i++)
257 	{
258 		strands[i] = new Strand(position, segs, dist);
259 		strands[i]->color = color;
260 		dsq->game->addRenderObject(strands[i], this->layer);
261 	}
262 	updateStrands(0);
263 }
264 
onAlwaysUpdate(float dt)265 void ScriptedEntity::onAlwaysUpdate(float dt)
266 {
267 	Entity::onAlwaysUpdate(dt);
268 
269 	updateStrands(dt);
270 
271 	if (!isEntityDead() && getState() != STATE_DEAD && getState() != STATE_DEATHSCENE && isPresent())
272 	{
273 		if (frozenTimer > 0)
274 		{
275 			pullEmitter.update(dt);
276 
277 			doFriction(dt, 50);
278 			updateCurrents(dt);
279 			updateMovement(dt);
280 
281 			if (hair)
282 			{
283 				setHairHeadPosition(position);
284 				updateHair(dt);
285 			}
286 
287 			if (skeletalSprite.isLoaded())
288 				dsq->game->handleShotCollisionsSkeletal(this);
289 			else
290 				dsq->game->handleShotCollisions(this);
291 		}
292 
293 		if (isPullable() && !fillGridFromQuad)
294 		{
295 			bool doCrush = false;
296 			crushDelay -= dt;
297 			if (crushDelay < 0)
298 			{
299 				crushDelay = 0.2;
300 				doCrush = true;
301 			}
302 			FOR_ENTITIES(i)
303 			{
304 				Entity *e = *i;
305 				if (e && e != this && e->life == 1 && e->ridingOnEntity != this)
306 				{
307 					if ((e->position - this->position).isLength2DIn(collideRadius + e->collideRadius))
308 					{
309 						if (this->isEntityProperty(EP_BLOCKER) && doCrush)
310 						{
311 							bool doit = !vel.isLength2DIn(64) || (e->position.y > position.y && vel.y > 0);
312 							if (doit)
313 							{
314 								if (e->getEntityType() == ET_ENEMY && e->isDamageTarget(DT_CRUSH))
315 								{
316 									DamageData d;
317 									d.damageType = DT_CRUSH;
318 									d.attacker = this;
319 									d.damage = 1;
320 									if (e->damage(d))
321 									{
322 										e->sound("RockHit");
323 										dsq->spawnParticleEffect("rockhit", e->position, 0, 0);
324 									}
325 									//e->push(vel, 0.2, 500, 0);
326 									Vector add = vel;
327 									add.setLength2D(5000*dt);
328 									e->vel += add;
329 								}
330 							}
331 						}
332 						Vector add = e->position - this->position;
333 						add.capLength2D(10000 * dt);
334 						e->vel += add;
335 						e->doCollisionAvoidance(dt, 3, 1);
336 					}
337 				}
338 			}
339 		}
340 
341 		if (isPullable())
342 		{
343 			Entity *followEntity = dsq->game->avatar;
344 			if (followEntity && dsq->game->avatar->pullTarget == this)
345 			{
346 				Vector dist = followEntity->position - this->position;
347 				if (dist.isLength2DIn(followEntity->collideRadius + collideRadius + 16))
348 				{
349 					vel = 0;
350 				}
351 				else if (!dist.isLength2DIn(800))
352 				{
353 					// break;
354 					vel.setZero();
355 					dsq->game->avatar->pullTarget->stopPull();
356 					dsq->game->avatar->pullTarget = 0;
357 				}
358 				else if (!dist.isLength2DIn(128))
359 				{
360 					Vector v = dist;
361 					int moveSpeed = 1000;
362 					moveSpeed = 4000;
363 					v.setLength2D(moveSpeed);
364 					vel += v*dt;
365 					setMaxSpeed(dsq->game->avatar->getMaxSpeed());
366 				}
367 				else
368 				{
369 					if (!vel.isZero())
370 					{
371 						Vector sub = vel;
372 						sub.setLength2D(getMaxSpeed()*maxSpeedLerp.x*dt);
373 						vel -= sub;
374 						if (vel.isLength2DIn(100))
375 							vel = 0;
376 					}
377 				}
378 				doCollisionAvoidance(dt, 2, 0.5);
379 			}
380 		}
381 	}
382 }
383 
updateStrands(float dt)384 void ScriptedEntity::updateStrands(float dt)
385 {
386 	if (strands.empty()) return;
387 	float angle = rotation.z;
388 	angle = (PI*(360-(angle-90)))/180.0;
389 	//angle = (180*angle)/PI;
390 	float sz = (strands.size()/2);
391 	for (int i = 0; i < strands.size(); i++)
392 	{
393 		float diff = (i-sz)*strandSpacing;
394 		if (diff < 0)
395 			strands[i]->position = position - Vector(sinf(angle)*fabsf(diff), cosf(angle)*fabsf(diff));
396 		else
397 			strands[i]->position = position + Vector(sinf(angle)*diff, cosf(angle)*diff);
398 		if (dt > 0)
399 			strands[i]->update(dt);
400 	}
401 }
402 
destroy()403 void ScriptedEntity::destroy()
404 {
405 	CollideEntity::destroy();
406 
407 	if (script)
408 	{
409 		dsq->scriptInterface.closeScript(script);
410 		script = 0;
411 	}
412 }
413 
song(SongType songType)414 void ScriptedEntity::song(SongType songType)
415 {
416 	if (script)
417 	{
418 		if (!script->call("song", this, int(songType)))
419 			debugLog(name + " : " + script->getLastError());
420 	}
421 }
422 
shiftWorlds(WorldType lastWorld,WorldType worldType)423 void ScriptedEntity::shiftWorlds(WorldType lastWorld, WorldType worldType)
424 {
425 	if (script)
426 	{
427 		if (!script->call("shiftWorlds", this, int(lastWorld), int(worldType)))
428 			debugLog(name + " : " + script->getLastError() + " shiftWorlds");
429 	}
430 }
431 
startPull()432 void ScriptedEntity::startPull()
433 {
434 	Entity::startPull();
435 	beforePullMaxSpeed = getMaxSpeed();
436 	becomeSolidDelay = false;
437 	debugLog("HERE!");
438 	if (isEntityProperty(EP_BLOCKER))
439 	{
440 		fillGridFromQuad = false;
441 		dsq->game->reconstructEntityGrid();
442 	}
443 	pullEmitter.load("Pulled");
444 	pullEmitter.start();
445 
446 	// HACK: move this to the lower level at some point
447 
448 	if (isEntityProperty(EP_BLOCKER))
449 	{
450 		FOR_ENTITIES(i)
451 		{
452 			Entity *e = *i;
453 			if (e != this && e->getEntityType() != ET_AVATAR && e->isv(EV_CRAWLING, 1))
454 			{
455 				if ((e->position - position).isLength2DIn(collideRadius+e->collideRadius+32))
456 				{
457 					debugLog(e->name + ": is now riding on : " + name);
458 					e->ridingOnEntity = this;
459 					e->ridingOnEntityOffset = e->position - position;
460 					e->ridingOnEntityOffset.setLength2D(collideRadius);
461 				}
462 			}
463 		}
464 	}
465 }
466 
sporesDropped(const Vector & pos,int type)467 void ScriptedEntity::sporesDropped(const Vector &pos, int type)
468 {
469 	if (script)
470 	{
471 		script->call("sporesDropped", this, pos.x, pos.y, type);
472 	}
473 }
474 
stopPull()475 void ScriptedEntity::stopPull()
476 {
477 	Entity::stopPull();
478 	pullEmitter.stop();
479 	setMaxSpeed(beforePullMaxSpeed);
480 }
481 
onUpdate(float dt)482 void ScriptedEntity::onUpdate(float dt)
483 {
484 	BBGE_PROF(ScriptedEntity_onUpdate);
485 
486 	CollideEntity::onUpdate(dt);
487 
488 	if (becomeSolidDelay)
489 	{
490 		if (vel.isLength2DIn(5))
491 		{
492 			if (!isEntityInside())
493 			{
494 				becomeSolid();
495 				becomeSolidDelay = false;
496 			}
497 		}
498 	}
499 
500 
501 	if (life != 1 || isEntityDead()) return;
502 
503 
504 	if (myTimer > 0)
505 	{
506 		myTimer -= dt;
507 		if (myTimer <= 0)
508 		{
509 			myTimer = 0;
510 			onExitTimer();
511 		}
512 	}
513 
514 	if (this->isEntityDead() || this->getState() == STATE_DEATHSCENE || this->getState() == STATE_DEAD)
515 	{
516 		return;
517 	}
518 	if (script)
519 	{
520 		if (!script->call("update", this, dt))
521 			debugLog(name + " : update : " + script->getLastError());
522 	}
523 
524 	if (numSegments > 0)
525 	{
526 		updateSegments(position, reverseSegments);
527 		updateAlpha(alpha.x);
528 	}
529 }
530 
resetTimer(float t)531 void ScriptedEntity::resetTimer(float t)
532 {
533 	myTimer = t;
534 }
535 
stopTimer()536 void ScriptedEntity::stopTimer()
537 {
538 	myTimer = 0;
539 }
540 
onExitTimer()541 void ScriptedEntity::onExitTimer()
542 {
543 	if (script)
544 	{
545 		if (!script->call("exitTimer", this))
546 			debugLog(this->name + " : " + script->getLastError() + " exitTimer");
547 	}
548 }
549 
onAnimationKeyPassed(int key)550 void ScriptedEntity::onAnimationKeyPassed(int key)
551 {
552 	if (script && animKeyFunc)
553 	{
554 		if (!script->call("animationKey", this, key))
555 		{
556 			debugLog(this->name + " : " + script->getLastError() + " animationKey");
557 			animKeyFunc = false;
558 		}
559 	}
560 
561 	Entity::onAnimationKeyPassed(key);
562 }
563 
lightFlare()564 void ScriptedEntity::lightFlare()
565 {
566 	if (script && !isEntityDead())
567 	{
568 		script->call("lightFlare", this);
569 	}
570 }
571 
canShotHit(const DamageData & d)572 bool ScriptedEntity::canShotHit(const DamageData &d)
573 {
574 	bool doDefault = true;
575 	if (script && canShotHitFunc)
576 	{
577 		if (!script->call("canShotHit", this, d.attacker, d.bone, int(d.damageType), d.damage, d.hitPos.x, d.hitPos.y, d.shot, &doDefault))
578 		{
579 			debugLog(name + ": canShotHit function failed");
580 			canShotHitFunc = false;
581 		}
582 	}
583 
584 	if (doDefault)
585 	{
586 		return Entity::canShotHit(d);
587 	}
588 
589 	return false;
590 }
591 
damage(const DamageData & d)592 bool ScriptedEntity::damage(const DamageData &d)
593 {
594 	if (d.damageType == DT_NONE)	return false;
595 	bool doDefault = true;
596 	if (script)
597 	{
598 		if (!script->call("damage", this, d.attacker, d.bone, int(d.damageType), d.damage, d.hitPos.x, d.hitPos.y, d.shot, &doDefault))
599 		{
600 			debugLog(name + ": damage function failed");
601 		}
602 	}
603 
604 	if (doDefault)
605 	{
606 		return Entity::damage(d);
607 	}
608 
609 	return false;
610 }
611 
songNote(int note)612 void ScriptedEntity::songNote(int note)
613 {
614 	Entity::songNote(note);
615 
616 	if (script && songNoteFunction)
617 	{
618 		if (!script->call("songNote", this, note))
619 		{
620 			songNoteFunction = false;
621 			debugLog(this->name + " : " + script->getLastError() + " songNote");
622 		}
623 	}
624 }
625 
songNoteDone(int note,float len)626 void ScriptedEntity::songNoteDone(int note, float len)
627 {
628 	Entity::songNoteDone(note, len);
629 	if (script && songNoteDoneFunction)
630 	{
631 		if (!script->call("songNoteDone", this, note, len))
632 		{
633 			songNoteDoneFunction = false;
634 			debugLog(this->name + " : " + script->getLastError() + " songNoteDone");
635 		}
636 	}
637 }
638 
becomeSolid()639 void ScriptedEntity::becomeSolid()
640 {
641 	//vel = 0;
642 	float oldRot = 0;
643 	bool doRot=false;
644 	Vector n = dsq->game->getWallNormal(position);
645 	if (!n.isZero())
646 	{
647 		oldRot = rotation.z;
648 		rotateToVec(n, 0);
649 		doRot = true;
650 	}
651 	fillGridFromQuad = true;
652 	dsq->game->reconstructEntityGrid();
653 
654 	FOR_ENTITIES(i)
655 	{
656 		Entity *e = *i;
657 		if (e->ridingOnEntity == this)
658 		{
659 			e->ridingOnEntity = 0;
660 			e->moveOutOfWall();
661 			// if can't get the rider out of the wall, kill it
662 			if (dsq->game->isObstructed(TileVector(e->position)))
663 			{
664 				e->setState(STATE_DEAD);
665 			}
666 		}
667 	}
668 
669 	if (doRot)
670 	{
671 		rotation.z = oldRot;
672 		rotateToVec(n, 0.01);
673 	}
674 }
675 
onHitWall()676 void ScriptedEntity::onHitWall()
677 {
678 	if (isEntityProperty(EP_BLOCKER) && !fillGridFromQuad && dsq->game->avatar->pullTarget != this)
679 	{
680 		becomeSolidDelay = true;
681 	}
682 
683 	if (isEntityProperty(EP_BLOCKER) && !fillGridFromQuad)
684 	{
685 		Vector n = dsq->game->getWallNormal(position);
686 		if (!n.isZero())
687 		{
688 			rotateToVec(n, 0.2);
689 		}
690 	}
691 
692 	CollideEntity::onHitWall();
693 
694 	if (script)
695 	{
696 		if (!script->call("hitSurface", this))
697 			debugLog(this->name + " : " + script->getLastError() + " hitSurface");
698 	}
699 }
700 
activate()701 void ScriptedEntity::activate()
702 {
703 	if (runningActivation) return;
704 	Entity::activate();
705 
706 	runningActivation = true;
707 	if (script)
708 	{
709 		if (!script->call("activate", this))
710 			luaDebugMsg("activate", script->getLastError());
711 	}
712 	runningActivation = false;
713 }
714 
shotHitEntity(Entity * hit,Shot * shot,Bone * bone)715 void ScriptedEntity::shotHitEntity(Entity *hit, Shot *shot, Bone *bone)
716 {
717 	Entity::shotHitEntity(hit, shot, bone);
718 
719 	if (script)
720 	{
721 		script->call("shotHitEntity", this, hit, shot, bone);
722 	}
723 }
724 
entityDied(Entity * e)725 void ScriptedEntity::entityDied(Entity *e)
726 {
727 	CollideEntity::entityDied(e);
728 
729 	if (script)
730 	{
731 		script->call("entityDied", this, e);
732 	}
733 }
734 
luaDebugMsg(const std::string & func,const std::string & msg)735 void ScriptedEntity::luaDebugMsg(const std::string &func, const std::string &msg)
736 {
737 	debugLog("luaScriptError: " + name + " : " + func + " : " + msg);
738 }
739 
onDieNormal()740 void ScriptedEntity::onDieNormal()
741 {
742 	Entity::onDieNormal();
743 	if (script)
744 	{
745 		script->call("dieNormal", this);
746 	}
747 }
748 
onDieEaten()749 void ScriptedEntity::onDieEaten()
750 {
751 	Entity::onDieEaten();
752 	if (script)
753 	{
754 		script->call("dieEaten", this);
755 	}
756 }
757 
onEnterState(int action)758 void ScriptedEntity::onEnterState(int action)
759 {
760 	CollideEntity::onEnterState(action);
761 
762 	if (script)
763 	{
764 		if (!script->call("enterState", this))
765 			luaDebugMsg("enterState", script->getLastError());
766 	}
767 	switch(action)
768 	{
769 	case STATE_DEAD:
770 		if (!isGoingToBeEaten())
771 		{
772 			doDeathEffects(manaBallAmount);
773 			dsq->spawnParticleEffect(deathParticleEffect, position);
774 			onDieNormal();
775 		}
776 		else
777 		{
778 			// eaten
779 			doDeathEffects(0);
780 			onDieEaten();
781 		}
782 		destroySegments(1);
783 
784 
785 		for (int i = 0; i < strands.size(); i++)
786 		{
787 			strands[i]->safeKill();
788 		}
789 		strands.clear();
790 
791 		// BASE ENTITY CLASS WILL HANDLE CLEANING UP HAIR
792 	break;
793 	}
794 }
795 
onExitState(int action)796 void ScriptedEntity::onExitState(int action)
797 {
798 
799 	if (script)
800 	{
801 		if (!script->call("exitState", this))
802 			luaDebugMsg("exitState", script->getLastError());
803 	}
804 
805 	CollideEntity::onExitState(action);
806 }
807 
deathNotify(RenderObject * r)808 void ScriptedEntity::deathNotify(RenderObject *r)
809 {
810 	if (script)
811 	{
812 		if (!script->call("deathNotify", this, r))
813 			luaDebugMsg("deathNotify", script->getLastError());
814 	}
815 	CollideEntity::deathNotify(r);
816 }
817 
818