sin-2015/goliath.cpp

1090 lines
23 KiB
C++
Raw Normal View History

1999-04-22 00:00:00 +00:00
/*
================================================================
GOLIATH
================================================================
Copyright (C) 1998 by 2015, Inc.
All rights reserved.
This source is may not be distributed and/or modified without
expressly written permission by 2015, Inc.
*/
#include "goliath.h"
#include "object.h"
#include "player.h"
#define GOLIATH_MAX_OBJECTS 20
//===============================================================
// GOLIATH
//===============================================================
Event EV_Goliath_InitThrowTime("throwtime");
Event EV_Goliath_MeleeForce( "meleeforce" );
Event EV_Goliath_SetLeftHand("setlefthand");
Event EV_Goliath_SetRightHand("setrighthand");
Event EV_Goliath_SetBothHands("setbothhands");
Event EV_Goliath_SetAttackStage("attackstage");
Event EV_Goliath_IfThrowHigh("ifthrowhigh");
Event EV_Goliath_IfThrowLow("ifthrowlow");
CLASS_DECLARATION(Actor, Goliath, "monster_goliath");
ResponseDef Goliath::Responses[] =
{
{&EV_Goliath_InitThrowTime, (Response)Goliath::InitThrowTime},
{&EV_Goliath_MeleeForce, (Response)Goliath::SetMeleeForce},
{&EV_Goliath_SetLeftHand, (Response)Goliath::SetLeftHand},
{&EV_Goliath_SetRightHand, (Response)Goliath::SetRightHand},
{&EV_Goliath_SetBothHands, (Response)Goliath::SetBothHands},
{&EV_Goliath_SetAttackStage, (Response)Goliath::SetAttackStage},
{&EV_Goliath_IfThrowHigh, (Response)Goliath::IfThrowHighEvent},
{&EV_Goliath_IfThrowLow, (Response)Goliath::IfThrowLowEvent},
{NULL, NULL}
};
Goliath::Goliath()
{
randomthrowtime = 0;
rubbletime = 0;
throwhigh = false;
leftdamage = 0;
leftforce = 0;
rightdamage = 0;
rightforce = 0;
hitsentient = false;
if(attackstage < 1 || attackstage > 2)
{
attackstage = 1;
}
if ( actorthread )
{
actorthread->Vars()->SetVariable( "attackstage", attackstage );
}
flags |= FL_POSTTHINK;
}
void Goliath::Prethink(void)
{
trace_t trace;
Vector tmpvec;
qboolean nowthrowing = false;
Entity *ent;
if(attackstage == 2)
{
if(randomthrowtime && (randomthrowtime < level.time))
{
// we want to throw something...
Entity *bestent;
float bestdist;
float dist;
float distance;
Vector delta;
distance = 300;
bestent = NULL;
bestdist = distance * distance;
ent = NULL;
while(ent = findradius(ent, worldorigin.vec3(), distance))
{
if(ent->isSubclassOf(ThrowObject))
{
delta = centroid - ent->centroid;
dist = delta * delta;
if(dist <= bestdist)
{
bestent = ent;
bestdist = dist;
}
}
}
if(bestent)
{
bestent = CheckObjectsAbove(bestent);
SetVariable("other", bestent);
if(DoAction("throwthing", false))
{
randomthrowtime = 0;
nowthrowing = true;
// easy his rumbling tendancies a bit
if(rubbletime)
rubbletime += 4 + G_Random(2) - skill->value;
}
}
}
// rumble timmer for when he goes to long wanting to
// throw something without anything around
if(randomthrowtime && !nowthrowing)
{
if(!rubbletime)
{
rubbletime = level.time + 5 + G_Random(2) - (skill->value * 1.5);
}
else if(rubbletime < level.time)
{
ScriptVariable *var;
int numobjects;
// check max objects at one time limit
var = levelVars.GetVariable("goliathobject_count");
if(var)
numobjects = var->intValue();
else
numobjects = 0;
if(numobjects < GOLIATH_MAX_OBJECTS)
{
// time to make more stuff to throw >)
if(DoAction("rumbleattack", false))
{
randomthrowtime = 0;
nowthrowing = true;
// ah, feel better now
rubbletime = 0;
}
}
else
{
// ease off his rubble making tendacies for a bit
rubbletime = level.time + 1 + G_Random(2);
}
}
}
}
// check for having a hissy hit and going into stage two
else
{
if(health < (max_health*0.5))
{
if(DoAction("pissedoff", false))
{
// yup, he's pissed
attackstage = 2;
nowthrowing = true;
// make sure rubble attack timmer is inited
rubbletime = 0;
}
}
// stage one throwing occures less often then stage two throwing
if(!nowthrowing && randomthrowtime && ((randomthrowtime + 1) < level.time))
{
// we want to throw something...
Entity *bestent;
float bestdist;
float dist;
float distance;
Vector delta;
distance = 320;
bestent = NULL;
bestdist = distance * distance;
ent = NULL;
while(ent = findradius(ent, worldorigin.vec3(), distance))
{
if(ent->isSubclassOf(ThrowObject))
{
delta = centroid - ent->centroid;
dist = delta * delta;
if(dist <= bestdist)
{
bestent = ent;
bestdist = dist;
}
}
}
if(bestent)
{
bestent = CheckObjectsAbove(bestent);
if(bestent)
{
SetVariable("other", bestent);
if(DoAction("throwthing", false))
{
randomthrowtime = 0;
nowthrowing = true;
}
}
}
}
}
if(!nowthrowing && randomthrowtime)
{
// if there's ever a throwable thing in our
// way, then get it out of our way.
tmpvec = worldorigin + move;
trace = G_Trace(worldorigin, mins, maxs, tmpvec, this, edict->clipmask, "Goliath::Prethink");
if((trace.fraction != 1) && (trace.ent))
{
// check if we ran into something we can throw
ent = trace.ent->entity;
if(ent->isSubclassOf(ThrowObject))
{
ent = CheckObjectsAbove(ent);
if(ent)
{
SetVariable("other", ent);
// if in attackstage 2, there's a chance we'll just smash it
if((attackstage == 2) && (G_Random() < 0.6))
{
DoAction("destroyobstruction", false);
}
else
{
DoAction("throwobstruction", false);
}
}
}
}
}
//
// call our superclass
//
Actor::Prethink();
}
#define HAND_SIZE 40
// here's where the hand collision detection is done
void Goliath::Postthink(void)
{
vec3_t trans[3];
vec3_t orient;
int groupindex;
int tri_num;
Vector pos, forward;
Vector offset;
Vector tmpvec;
Vector handmins, handmaxs;
Vector min, max;
float handdiff;
int num;
edict_t *touch[MAX_EDICTS];
handmins = Vector(-HAND_SIZE, -HAND_SIZE, -HAND_SIZE);
handmaxs = Vector(HAND_SIZE, HAND_SIZE, HAND_SIZE);
if(rightdamage || rightforce)
{
if(!gi.GetBoneInfo(edict->s.modelindex, "gun", &groupindex, &tri_num, orient))
{
// couldn't get proper bone info
gi.dprintf("Couldn't find Goliath's gun bone\n");
return;
}
AngleVectors(orient, forward.vec3(), NULL, NULL);
// get hand position
offset = vec_zero;
gi.GetBoneTransform(edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim,
edict->s.frame, edict->s.scale, trans, offset.vec3());
MatrixTransformVector(offset.vec3(), orientation, pos.vec3());
pos += worldorigin;
// check at an interpolated position if hand moved enough from last frame
if(lastrightpos != vec_zero)
{
tmpvec = lastrightpos - pos;
handdiff = tmpvec.length();
if(handdiff > (HAND_SIZE*3))
{
tmpvec = (lastrightpos + pos)*0.5;
min = tmpvec + handmins;
max = tmpvec + handmaxs;
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS);
if(num)
DoHandHits(tmpvec, forward, rightdamage, rightforce, touch, num);
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID);
if(num)
DoHandHits(tmpvec, forward, rightdamage, rightforce, touch, num);
}
}
// check current hand position
min = pos + handmins;
max = pos + handmaxs;
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS);
if(num)
DoHandHits(pos, forward, rightdamage, rightforce, touch, num);
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID);
if(num)
DoHandHits(pos, forward, rightdamage, rightforce, touch, num);
// save current hand position
lastrightpos = pos;
}
else
{
// clear out last hand position
lastrightpos = vec_zero;
}
if(leftdamage || leftforce)
{
if(!gi.GetBoneInfo(edict->s.modelindex, "leftgun", &groupindex, &tri_num, orient))
{
// couldn't get proper bone info
gi.dprintf("Couldn't find Goliath's leftgun bone\n");
return;
}
AngleVectors(orient, forward.vec3(), NULL, NULL);
// get hand position
offset = vec_zero;
gi.GetBoneTransform(edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim,
edict->s.frame, edict->s.scale, trans, offset.vec3());
MatrixTransformVector(offset.vec3(), orientation, pos.vec3());
pos += worldorigin;
// check at an interpolated position if hand moved enough from last frame
if(lastleftpos != vec_zero)
{
tmpvec = lastleftpos - pos;
handdiff = tmpvec.length();
if(handdiff > (HAND_SIZE*3))
{
tmpvec = (lastleftpos + pos)*0.5;
min = tmpvec + handmins;
max = tmpvec + handmaxs;
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS);
if(num)
DoHandHits(tmpvec, forward, leftdamage, leftforce, touch, num);
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID);
if(num)
DoHandHits(tmpvec, forward, leftdamage, leftforce, touch, num);
}
}
// check current hand position
min = pos + handmins;
max = pos + handmaxs;
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS);
if(num)
DoHandHits(pos, forward, leftdamage, leftforce, touch, num);
num = gi.BoxEdicts(min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID);
if(num)
DoHandHits(pos, forward, leftdamage, leftforce, touch, num);
// save current hand position
lastleftpos = pos;
}
else
{
// clear out last hand position
lastleftpos = vec_zero;
}
}
void Goliath::InitThrowTime(Event *ev)
{
float mintime, maxtime, timediff;
// if fewer than 2 numbers are given, clear timmer
if(ev->NumArgs() < 2)
{
randomthrowtime = 0;
return;
}
mintime = ev->GetFloat(1);
maxtime = ev->GetFloat(2);
timediff = maxtime - mintime;
randomthrowtime = level.time + mintime + G_Random(timediff);
randomthrowtime = level.time + G_Random(timediff);
}
Entity *Goliath::CheckObjectsAbove(Entity *ent)
{
Vector tmpvec, tmpvec2;
trace_t trace;
int mask, i;
Entity *newent;
float tmpflt;
// make sure we're passed a good thing
if(!ent)
return NULL;
if((ent->health <= 0) || (!ent->isSubclassOf(ThrowObject)))
return NULL;
// check for a better thing on the thing
mask = MASK_PROJECTILE;
// first try a full size trace
tmpvec = ent->worldorigin + Vector(0, 0, 16);
trace = G_Trace(ent->worldorigin, ent->mins, ent->maxs, tmpvec, ent, mask, "Goliath::CheckObjectsAbove");
// check for something good
if(trace.ent)
{
newent = trace.ent->entity;
// check for a stackable object that's above us
if((newent->spawnflags & 8) && (newent->worldorigin.z > ent->worldorigin.z))
{
throwhigh = true;
return newent;
}
}
// try several different spots
for(i = 0; i < 13; i++)
{
tmpvec = ent->worldorigin;
switch(i)
{
// corners
case 1:
tmpvec.x += ent->mins.x + 8;
tmpvec.y += ent->mins.y + 8;
break;
case 2:
tmpvec.x += ent->mins.x + 8;
tmpvec.y += ent->maxs.y - 8;
break;
case 3:
tmpvec.x += ent->maxs.x - 8;
tmpvec.y += ent->mins.y + 8;
break;
case 4:
tmpvec.x += ent->maxs.x - 8;
tmpvec.y += ent->maxs.y - 8;
break;
// edge middles
case 5:
tmpvec.x += ent->mins.x + 8;
break;
case 6:
tmpvec.x += ent->maxs.x - 8;
break;
case 7:
tmpvec.y += ent->mins.y + 8;
break;
case 8:
tmpvec.y += ent->maxs.y - 8;
break;
}
tmpvec2 = tmpvec + Vector(0, 0, 16);
trace = G_Trace(tmpvec, Vector(-4, -4, 0), Vector(4, 4, ent->maxs.z - 1), tmpvec2, ent, mask, "Goliath::CheckObjectsAbove");
// check for something good
if(trace.ent)
{
newent = trace.ent->entity;
// check for a stackable object that's above us
if(newent->worldorigin.z > ent->worldorigin.z)
{
throwhigh = true;
return newent;
}
}
}
// couldn't find anything, so stick with that we've got
// check our object's pichup height
tmpflt = ent->worldorigin.z - worldorigin.z;
if(tmpflt > 32)
throwhigh = true;
else
throwhigh = false;
return ent;
}
//===============================================================
// melee attack stuff
void Goliath::SetMeleeForce (Event *ev)
{
melee_force = ev->GetFloat(1);
melee_force *= edict->s.scale;
}
void Goliath::SetLeftHand(Event *ev)
{
leftdamage = ev->GetFloat(1);
leftforce = ev->GetFloat(2);
}
void Goliath::SetRightHand(Event *ev)
{
rightdamage = ev->GetFloat(1);
rightforce = ev->GetFloat(2);
}
void Goliath::SetBothHands(Event *ev)
{
rightdamage = ev->GetFloat(1);
rightforce = ev->GetFloat(2);
leftdamage = rightdamage;
leftforce = rightforce;
}
void Goliath::DoHandHits(Vector pos, Vector dir, float handdamage, float handforce, edict_t *touch[MAX_EDICTS], int num)
{
edict_t *hit;
Entity *ent;
int i;
float damage;
// horizontalize the direction
dir.z = 0;
dir.normalize();
for(i = 0; i < num; i++)
{
hit = touch[i];
if(!hit->inuse)
continue;
assert(hit->entity);
ent = hit->entity;
if(ent == this)
continue;
if(ent->getSolidType() == SOLID_BSP)
continue;
// so goliaths don't hurt each other
if(ent->isSubclassOf(Goliath))
continue;
if(ent->isSubclassOf(Sentient) && hitsentient)
continue;
if(ent->takedamage)
{
if(ent->isSubclassOf(Sentient))
{
damage = melee_damage*handdamage;
hitsentient = true;
}
else
{
damage = ent->max_health * 10;
// make a hit sound
ent->RandomGlobalSound("impact_smallexplosion", 0.5, CHAN_BODY);
}
ent->Damage( this, this, (int)damage, pos,
vec_zero, vec_zero, 0, DAMAGE_NO_KNOCKBACK, MOD_MUTANTHANDS, -1, -1, 1.0f );
ent->velocity -= dir * (melee_force * handforce);
if(ent->velocity.z < 50)
ent->velocity.z = 50;
ent->groundentity = NULL;
}
}
}
//===============================================================
void Goliath::SetAttackStage(Event *ev)
{
attackstage = ev->GetInteger(1);
if(attackstage < 1)
attackstage = 1;
else if(attackstage > 2)
attackstage = 2;
if ( actorthread )
{
actorthread->Vars()->SetVariable( "attackstage", attackstage );
}
}
void Goliath::IfThrowHighEvent(Event *ev)
{
ScriptThread *thread;
thread = ev->GetThread();
assert(thread);
if(!thread)
{
return;
}
if(throwhigh)
{
thread->ProcessCommandFromEvent(ev, 1);
}
}
void Goliath::IfThrowLowEvent(Event *ev)
{
ScriptThread *thread;
thread = ev->GetThread();
assert(thread);
if(!thread)
{
return;
}
if(!throwhigh)
{
thread->ProcessCommandFromEvent(ev, 1);
}
}
//===============================================================
/****************************************************************************
RumbleAttack Class Definition
Goliath does an attack that causes the player to get jittered around
and also causes rubble to fall from the ceiling
****************************************************************************/
CLASS_DECLARATION( Behavior, RumbleAttack, NULL );
Event EV_RumbleAttack_DoRumble("dorumble");
ResponseDef RumbleAttack::Responses[] =
{
{&EV_Behavior_Args, (Response)RumbleAttack::SetArgs},
{&EV_Behavior_AnimDone, (Response)RumbleAttack::AnimDone},
{&EV_RumbleAttack_DoRumble,(Response)RumbleAttack::DoRumble},
{NULL, NULL}
};
void RumbleAttack::ShowInfo (Actor &self)
{
Behavior::ShowInfo(self);
gi.printf("\nanim: %s\n", anim.c_str());
gi.printf("animdone: %d\n", animdone);
gi.printf("\nrubbletarget: %s\n", rubbletarget.c_str());
gi.printf("rubblenums: %d\n", rubblenums);
}
void RumbleAttack::Begin (Actor &self)
{
if(!anim.length())
{
anim = "rumble";
}
animdone = false;
// start off the rumbling animation
if(self.HasAnim(anim.c_str()))
{
self.SetAnim(anim.c_str(), EV_Actor_NotifyBehavior);
}
else
{
// no animation, so don't do it
self.SetAnim("idle");
animdone = true;
}
}
void RumbleAttack::SetArgs (Event *ev)
{
anim = ev->GetString(2);
rubbletarget = ev->GetString(3);
}
void RumbleAttack::AnimDone (Event *ev)
{
animdone = true;
}
qboolean RumbleAttack::Evaluate (Actor &self)
{
// when the animation is finished, the behaivior is finished
if(animdone)
return false;
return true;
}
void RumbleAttack::End (Actor &self)
{
self.SetAnim("idle");
}
void RumbleAttack::DoRumble(Event *ev)
{
str targ;
int num, i;
Entity *ent;
Player *player;
Event *event;
targ = rubbletarget;
if(targ.length())
{
num = 0;
do
{
num = G_FindTarget(num, targ.c_str());
if(!num)
{
break;
}
ent = G_GetEntity(num);
event = new Event(EV_Activate);
event->AddEntity(ev->GetEntity(1));
ent->PostEvent(event, 0);
}
while(1);
}
// jitter the player around
for(i = 1; i <= maxclients->value; i++)
{
if(!g_edicts[i].inuse || !g_edicts[i].entity)
{
continue;
}
player = (Player *)(g_edicts[i].entity);
player->SetAngleJitter(12, 5, 1);
player->SetOffsetJitter(10, 2.5, 1);
}
}
/****************************************************************************
GoliathMelee Class Definition
****************************************************************************/
CLASS_DECLARATION( Behavior, GoliathMelee, NULL );
ResponseDef GoliathMelee::Responses[] =
{
{&EV_Behavior_AnimDone, (Response)GoliathMelee::AnimDone},
{NULL, NULL}
};
GoliathMelee::GoliathMelee()
{
}
void GoliathMelee::ShowInfo (Actor &self)
{
Behavior::ShowInfo( self );
gi.printf("\naim:\n");
turnto.ShowInfo(self);
gi.printf( "animdone: %d\n", animdone );
}
void GoliathMelee::Begin (Actor &self)
{
mode = 0;
animdone = false;
// clear Goliaths hitsentient value
((Goliath *)&self)->hitsentient = false;
}
void GoliathMelee::SetArgs (Event *ev)
{
}
void GoliathMelee::AnimDone (Event *ev)
{
animdone = true;
}
qboolean GoliathMelee::Evaluate (Actor &self)
{
if(animdone)
{
return false;
}
if(!self.has_melee || !self.currentEnemy)
{
return false;
}
if(mode == 0)
{
float r;
Vector delta;
delta = self.currentEnemy->centroid - self.centroid;
r = delta.length();
if(r > self.melee_range)
{
return false;
}
r = delta.toYaw();
turnto.SetDirection(r);
turnto.SetTolerance(10);
if(turnto.Evaluate(self))
{
return true;
}
// melee
self.SetAnim("melee", EV_Actor_NotifyBehavior);
self.Chatter("snd_attacktaunt", 5);
mode = 1;
}
return true;
}
void GoliathMelee::End (Actor &self)
{
turnto.End(self);
self.SetAnim("idle");
}
/****************************************************************************
Goliath Pickup Behavior Class Definition
****************************************************************************/
CLASS_DECLARATION( Behavior, GoliathPickupAndThrow, NULL );
extern Event EV_PickupAndThrow_Pickup;
extern Event EV_PickupAndThrow_Throw;
ResponseDef GoliathPickupAndThrow::Responses[] =
{
{&EV_Behavior_Args, (Response)GoliathPickupAndThrow::SetArgs},
{&EV_Behavior_AnimDone, (Response)GoliathPickupAndThrow::AnimDone},
{&EV_PickupAndThrow_Pickup, (Response)GoliathPickupAndThrow::Pickup},
{&EV_PickupAndThrow_Throw, (Response)GoliathPickupAndThrow::Throw},
{NULL, NULL}
};
GoliathPickupAndThrow::GoliathPickupAndThrow()
{
}
void GoliathPickupAndThrow::ShowInfo (Actor &self)
{
Behavior::ShowInfo( self );
gi.printf( "\naim:\n" );
aim.ShowInfo( self );
gi.printf( "\nmode: %d\n", mode );
gi.printf( "\nanim: %s\n", anim.c_str() );
gi.printf( "animdone: %d\n", animdone );
if ( pickup_target )
{
gi.printf( "\npickup_target: #%d '%s'\n", pickup_target->entnum, pickup_target->targetname.c_str() );
}
else
{
gi.printf( "\npickup_target: NULL\n" );
}
}
void GoliathPickupAndThrow::Begin (Actor &self)
{
mode = 0;
animdone = false;
if(!anim.length())
{
anim = "pickup";
}
// turn towards the object
if(pickup_target)
{
turnto.SetTarget(pickup_target);
turnto.SetTolerance(20);
turnto.Begin(self);
}
}
void GoliathPickupAndThrow::SetArgs (Event *ev)
{
pickup_target = ev->GetEntity( 2 );
if(ev->NumArgs() > 2)
{
anim = ev->GetString(3);
}
}
void GoliathPickupAndThrow::AnimDone (Event *ev)
{
animdone = true;
}
void GoliathPickupAndThrow::Pickup (Event *ev)
{
Entity * ent;
Event * e;
ent = ev->GetEntity( 1 );
if ( pickup_target )
{
e = new Event( EV_ThrowObject_Pickup );
e->AddEntity( ent );
e->AddString( ev->GetString( 2 ) );
pickup_target->ProcessEvent( e );
turnto.End(*((Actor *)ent));
}
}
void GoliathPickupAndThrow::Throw (Event *ev)
{
Actor * act;
Event * e;
act = (Actor *)ev->GetEntity( 1 );
if ( pickup_target )
{
if ( !act->currentEnemy )
return;
e = new Event( EV_ThrowObject_Throw );
e->AddEntity( act );
e->AddFloat( 800 );
e->AddEntity( act->currentEnemy );
e->AddFloat( 1 );
pickup_target->ProcessEvent( e );
}
}
qboolean GoliathPickupAndThrow::Evaluate (Actor &self)
{
Event * ev;
if ( !self.currentEnemy || !pickup_target )
return false;
switch( mode )
{
case 0 :
if ( self.HasAnim( anim.c_str() ) )
{
animdone = false;
self.SetAnim( anim.c_str(), EV_Actor_NotifyBehavior );
mode = 1;
}
else
{
// skip the pickup animation
ev = new Event( EV_PickupAndThrow_Pickup );
ev->AddEntity( &self );
ev->AddString( "gun" );
ProcessEvent( ev );
animdone = true;
}
case 1 :
if ( !animdone )
break;
aim.Begin( self );
mode = 2;
if ( self.HasAnim( "throw_aim" ) )
{
self.SetAnim( "throw_aim", EV_Actor_NotifyBehavior );
}
else
{
self.SetAnim( "idle" );
}
case 2 :
// aim towards our target
aim.SetTarget( self.currentEnemy );
if ( aim.Evaluate( self ) )
{
break;
}
mode = 3;
case 3 :
// Throwing
mode = 4;
if ( self.HasAnim( "throw" ) )
{
animdone = false;
self.SetAnim( "throw", EV_Actor_NotifyBehavior );
mode = 4;
}
else
{
// skip the pickup animation
ev = new Event( EV_PickupAndThrow_Throw );
ev->AddEntity( &self );
ProcessEvent( ev );
animdone = true;
}
break;
case 4 :
if ( !animdone )
break;
return false;
break;
}
return true;
}
void GoliathPickupAndThrow::End (Actor &self)
{
aim.End( self );
turnto.End(self);
self.SetAnim( "idle" );
}