/////////////////////////////////////////////
// //
// METAL GEAR PATROLING ENEMY SOLDIER //
// //
/////////////////////////////////////////////
const int CMB_AUTOWARP = 3; //An autowarp A combo.
const int UNIFORM_TILE = 6240; // First of 4 tiles for the Enemy Uniform
// In this order- down, left, up, right.
item script Metal_Gear_Enemy_Uniform{
void run(){
//If not wearing the Uniform, put it on.
//Otherwise, take it off.
if(!Uniform_On)Uniform_On = true;
else{
Uniform_On = false;
}
}
}
void Wear_Uniform(){
int MGUniform_Tile;
while(true){
//Change the Uniform, depending on your direction
if(Link->Dir == DIR_DOWN)MGUniform_Tile = UNIFORM_TILE;
else if(Link->Dir == DIR_LEFT)MGUniform_Tile = UNIFORM_TILE+1;
else if(Link->Dir == DIR_UP)MGUniform_Tile = UNIFORM_TILE+2;
else if(Link->Dir == DIR_RIGHT)MGUniform_Tile = UNIFORM_TILE+3;
if(Uniform_On)Screen->DrawTile(3, Link->X, Link->Y-16, MGUniform_Tile, 1, 2, 4, 16, 32, 0, 0, 0, 0, true, 1284);
Waitframe();
}
}
// The guard's speed if D2 is left at 0.
const int CG_DEFAULT_SPEED = 0.75;
// The guard will play this sound when it sees Link.
// If this is set to 0, no sound will be played.
const int CG_SOUND = 71; // The is the Metal Gear Enemy Alert SFX I have
bool CG_FIND_LINK_IF_CLOSE = true;
// How close Link must be to be found when out of sight.
const int CG_FIND_LINK_DISTANCE = 24;
// The angle of the guard's field of vision.
const int CG_VISION_ANGLE = 75; // You may want to reduce this slightly.
// The maximum distance at which the guard can see Link.
const int CG_VISION_RANGE = 160; // You'll almost certainly want to reduce this, since the guard seems to be able to see you anywhere.
// The guard will walk along a path of this flag
const int CG_FLAG_WALK = 8; // Changed to raft flag to see if it any flag could be used.
// Apparently true.
// This flag blocks the guard's vision
const int CG_FLAG_BLOCK = 103; // Changed to raft bounce flag for same reason.
// Movement types
const int CG_MV_PATROL_LEFT = 0;
const int CG_MV_PATROL_RIGHT = 1;
const int CG_MV_STAND = 2;
const int CG_MV_ROTATE_CW = 3;
const int CG_MV_ROTATE_CCW = 4;
const int CG_MV_LEFT_AND_RIGHT = 5;
// Reaction types
const int CG_RE_WARP = 0;
const int CG_RE_BECOME_ENEMY = 1;
// ffc->Misc[] index
const int CG_IDX_ALERT = 0;
ffc script Metal_Gear_Patrolling_Guard
{
void run(int movement, int dir, int reaction, float speed,
int message, int enemyID)
{
// First standing and walking combos
int standCombo=this->Data-dir;
int walkCombo=standCombo+4;
if(speed==0)
speed=CG_DEFAULT_SPEED;
// Stand in place
if(movement==CG_MV_STAND)
{
while(true)
CGWaitframe(this, dir, reaction, message, enemyID);
}
// Turn in place
else if(movement==CG_MV_ROTATE_CW || movement==CG_MV_ROTATE_CCW)
{
while(true)
{
CGWaitframes(this, dir, reaction, message, enemyID, 120/speed);
if(movement==CG_MV_ROTATE_CW)
dir=RightFrom(dir);
else // CCW
dir=LeftFrom(dir);
this->Data=standCombo+dir;
}
}
// Turn left and right
else if(movement==CG_MV_LEFT_AND_RIGHT)
{
// 3 0 1 2 = left right right left
for(int i=3; true; i=(i+1)%4)
{
CGWaitframes(this, dir, reaction, message, enemyID, 120/speed);
if(i>=2)
dir=LeftFrom(dir);
else
dir=RightFrom(dir);
this->Data=standCombo+dir;
}
}
else // Patrolling
{
int walkCombo=standCombo+4;
int dist=GetWalkDistance(this, dir);
int step;
while(true)
{
this->Data=walkCombo+dir;
// Walk as far as possible
for(dist; dist>0; dist-=speed)
{
step=Min(speed, dist);
if(dir==DIR_UP)
this->Y-=step;
else if(dir==DIR_DOWN)
this->Y+=step;
else if(dir==DIR_LEFT)
this->X-=step;
else // Right
this->X+=step;
CGWaitframe(this, dir, reaction, message, enemyID);
}
// Can't go any farther; turn
if(movement==CG_MV_PATROL_LEFT)
{
// Try turning left
dir=LeftFrom(dir);
dist=GetWalkDistance(this, dir);
if(dist>0)
continue;
// Can't go that way; try right
dir=OppositeDir(dir);
dist=GetWalkDistance(this, dir);
if(dist>0)
continue;
// Can't go that way, either; just turn around
dir=RightFrom(dir);
dist=GetWalkDistance(this, dir);
}
else // Patrol/right
{
// Same thing, opposite directions
dir=RightFrom(dir);
dist=GetWalkDistance(this, dir);
if(dist>0)
continue;
dir=OppositeDir(dir);
dist=GetWalkDistance(this, dir);
if(dist>0)
continue;
dir=LeftFrom(dir);
dist=GetWalkDistance(this, dir);
}
}
}
}
bool CanSeeLink(int dir, ffc this)
{
// Is Link invisible?
if(Link->Invisible)
return false;
if(Uniform_On)return false;
if(this->X==Link->X && this->Y==Link->Y) // It can happen
return true;
// Check the distance
float distance=Distance(this->X, this->Y, Link->X, Link->Y);
if(distance>CG_VISION_RANGE)
return false;
if(CG_FIND_LINK_IF_CLOSE && distance<=CG_FIND_LINK_DISTANCE)
return true;
// See if the angle's okay
float angle=ArcTan(Link->X-this->X, Link->Y-this->Y)*180/PI;
float adjustedAngle=angle;
if(dir==DIR_UP)
adjustedAngle+=90;
else if(dir==DIR_DOWN)
adjustedAngle-=90;
else if(dir==DIR_LEFT)
adjustedAngle+=180;
if(adjustedAngle>180)
adjustedAngle-=360;
if(Abs(adjustedAngle)>CG_VISION_ANGLE/2)
return false;
// Check each tile along the line
float x=this->X+8;
float y=this->Y+8;
float endX=Link->X+8;
float endY=Link->Y+8;
float xDiff=endX-x;
float yDiff=endY-y;
float xRatio;
float yRatio;
int nextX;
int nextY;
float top=Min(y, endY)&0xF0;
float bottom=(Max(y, endY)&0xF0)+15;
float left=Min(x, endX)&0xF0;
float right=(Max(x, endX)&0xF0)+15;
bool leftToRight=x<endX;
bool topToBottom=y<endY;
bool nextIsX;
xRatio=xDiff;
if(yDiff!=0)
xRatio/=yDiff;
yRatio=yDiff;
if(xDiff!=0)
yRatio/=xDiff;
while(y>=top && y<=bottom && x>=left && x<=right)
{
if(ComboFI(x,y,CG_FLAG_BLOCK))
return false;
// Find the next tile edge each way
if(leftToRight)
nextX=(x&0xF0)+16;
else
nextX=(x&0xF0)-1;
if(topToBottom)
nextY=(y&0xF0)+16;
else
nextY=(y&0xF0)-1;
// Does X or Y come next?
if(xRatio==0)
nextIsX=false;
else if(yRatio==0)
nextIsX=true;
else
nextIsX=Abs((nextX-x)/xDiff)<Abs((nextY-y)/yDiff);
// Move to the next tile
if(nextIsX)
{
y+=yRatio*(nextX-x);
x=nextX;
}
else
{
x+=xRatio*(nextY-y);
y=nextY;
}
}
return true;
}
// Called instead of Waitframe(). Handles things that need to be done
// every frame.
void CGWaitframe(ffc this, int dir, int reaction, int message, int enemyID)
{
// First, see if someone else just saw Solid Snake or Link
if(this->Misc[CG_IDX_ALERT]==1)
{
// A warping guard won't react
if(reaction==CG_RE_WARP)
this->Misc[CG_IDX_ALERT]=0;
// An enemy guard will change
else
{
npc enemy=Screen->CreateNPC(enemyID);
enemy->X=this->X;
enemy->Y=this->Y;
enemy->Dir=dir;
this->Data=0;
Quit();
}
}
// See Solid Snake or Link?
if(CanSeeLink(dir, this))
{
// Alert the others, play the sound, and show the message
ffc other;
for(int i=1; i<=32; i++)
{
other=Screen->LoadFFC(i);
if(other->Script==this->Script)
other->Misc[CG_IDX_ALERT]=1;
}
Game->PlaySound(CG_SOUND);
if(message>0)
{
Screen->Message(message);
// If this is a warping guard, don't let Link move
if(reaction==CG_RE_WARP)
{
while(!Link->InputA)
WaitNoAction();
}
}
if(reaction==CG_RE_WARP)
this->Data = CMB_AUTOWARP;
//This doesn't always seem to work. I think it might because of the enormous vision range of the guards.
else // Become an enemy
{
npc enemy=Screen->CreateNPC(enemyID);
enemy->X=this->X;
enemy->Y=this->Y;
enemy->Dir=dir;
this->Data=0;
Quit();
}
}
Waitframe();
}
void CGWaitframes(ffc this, int dir, int reaction, int message, int enemyID, int numFrames)
{
for(numFrames; numFrames>0; numFrames--)
CGWaitframe(this, dir, reaction, message, enemyID);
}
// Find the Metal Gear Patroling Soldier's new direction if it turns left
int LeftFrom(int dir)
{
if(dir==DIR_UP)
return DIR_LEFT;
else if(dir==DIR_DOWN)
return DIR_RIGHT;
else if(dir==DIR_LEFT)
return DIR_DOWN;
else
return DIR_UP;
}
// Find the Metal Gear Patroling Soldier's new direction if it turns right
int RightFrom(int dir)
{
if(dir==DIR_UP)
return DIR_RIGHT;
else if(dir==DIR_DOWN)
return DIR_LEFT;
else if(dir==DIR_LEFT)
return DIR_UP;
else
return DIR_DOWN;
}
// Find how far a Metal Gear Patroling Soldier can walk
int GetWalkDistance(ffc this, int dir)
{
int numFlags=0;
int x=this->X;
int y=this->Y;
// Count walk flags in the given direction; assume the guard's on a flag already
while(true)
{
// Check every 16 pixels
if(dir==DIR_UP)
y-=16;
else if(dir==DIR_DOWN)
y+=16;
else if(dir==DIR_LEFT)
x-=16;
else // Right
x+=16;
// Is the next tile offscreen? Can't go farther than that.
if(x<0 || x>255 || y<0 || y>176)
return numFlags*16;
// No more flags? That's it.
else if(!ComboFI(x,y,CG_FLAG_WALK))
return numFlags*16;
else
numFlags++;
}
}
// Metal Gear Patroling Soldier Walks forward
void Move(int dir, float speed, ffc this)
{
if(dir==DIR_UP)
this->Y-=speed;
else if(dir==DIR_DOWN)
this->Y+=speed;
else if(dir==DIR_LEFT)
this->X-=speed;
else // Right
this->X+=speed;
}
}
With this line in the Global Script above Waitframe();
if(Uniform_On)Wear_Uniform(); // This will be for Enemy Uniform or box