diff --git a/source/enemy/ngeneric.cpp b/source/enemy/ngeneric.cpp index 325d3ae57..13f5c2e86 100644 --- a/source/enemy/ngeneric.cpp +++ b/source/enemy/ngeneric.cpp @@ -192,7 +192,7 @@ bool CNpcEnemy::processGroundCollisionReverse( s32 *moveX, s32 *moveY ) return( xBlocked | yBlocked ); } -void CNpcEnemy::processGenericFixedPathMove( int _frames, s32 *moveX, s32 *moveY, s32 *moveVel, s32 *moveDist ) +u8 CNpcEnemy::processGenericFixedPathMove( int _frames, s32 *moveX, s32 *moveY, s32 *moveVel, s32 *moveDist ) { bool pathComplete; bool waypointChange; @@ -302,6 +302,8 @@ void CNpcEnemy::processGenericFixedPathMove( int _frames, s32 *moveX, s32 *moveY //processGroundCollisionReverse( moveX, moveY ); } + + return( waypointChange ); } void CNpcEnemy::processGenericFixedPathWalk( int _frames, s32 *moveX, s32 *moveY ) diff --git a/source/enemy/npc.h b/source/enemy/npc.h index 0b5640e8e..c2fe211a0 100644 --- a/source/enemy/npc.h +++ b/source/enemy/npc.h @@ -260,7 +260,7 @@ protected: void processGenericGotoTarget( int _frames, s32 xDist, s32 yDist, s32 speed ); void processGenericGetUserDist( int _frames, s32 *distX, s32 *distY ); - void processGenericFixedPathMove( int _frames, s32 *moveX, s32 *moveY, s32 *moveVel, s32 *moveDist ); + u8 processGenericFixedPathMove( int _frames, s32 *moveX, s32 *moveY, s32 *moveVel, s32 *moveDist ); void processGenericFixedPathWalk( int _frames, s32 *moveX, s32 *moveY ); bool processGroundCollisionReverse( s32 *moveX, s32 *moveY ); virtual void processEnemyCollision( CThing *thisThing ); diff --git a/source/enemy/npcdata.cpp b/source/enemy/npcdata.cpp index ab83e7ded..23875a291 100644 --- a/source/enemy/npcdata.cpp +++ b/source/enemy/npcdata.cpp @@ -435,7 +435,7 @@ CNpcEnemy::NPC_DATA CNpcEnemy::m_data[NPC_UNIT_TYPE_MAX] = NPC_TIMER_NONE, false, 3, - 256, + 64, DETECT_ALL_COLLISION, DAMAGE__SHOCK_ENEMY, 1, diff --git a/source/enemy/npcpath.cpp b/source/enemy/npcpath.cpp index 9fc666a23..76c1957fb 100644 --- a/source/enemy/npcpath.cpp +++ b/source/enemy/npcpath.cpp @@ -45,6 +45,20 @@ bool CNpcPath::isPointNear( DVECTOR testPos, s32 *xDist, s32 *yDist ) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void CNpcPath::getCurrentWaypointPos( DVECTOR *waypointPos ) +{ + u16 *waypoint = waypointPtr; + waypoint += 2 * currentWaypoint; + + waypointPos->vx = ( *waypoint << 4 ) + 8; + + waypoint++; + + waypointPos->vy = ( *waypoint << 4 ) + 16; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void CNpcPath::initPath() { //waypoint = NULL; diff --git a/source/enemy/npcpath.h b/source/enemy/npcpath.h index 368132f55..41c2a1e4d 100644 --- a/source/enemy/npcpath.h +++ b/source/enemy/npcpath.h @@ -45,6 +45,7 @@ public: void setPathExtents(); u16 *getWaypointPtr() { return( waypointPtr ); } bool isPointNear( DVECTOR testPos, s32 *xDist, s32 *yDist ); + void getCurrentWaypointPos( DVECTOR *waypointPos ); private: NPC_PATH_TYPE pathType; diff --git a/source/enemy/nssnake.cpp b/source/enemy/nssnake.cpp index 463fff95f..19a05fc81 100644 --- a/source/enemy/nssnake.cpp +++ b/source/enemy/nssnake.cpp @@ -166,6 +166,7 @@ void CNpcSeaSnakeEnemy::postInit() m_movementTimer = 2 * GameState::getOneSecondInFrames(); m_collTimer = 0; m_meterOn=false; + m_turnDir = 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -249,7 +250,161 @@ void CNpcSeaSnakeEnemy::processMovement( int _frames ) } } - processGenericFixedPathMove( _frames, &moveX, &moveY, &moveVel, &moveDist ); + switch( m_turnDir ) + { + case NPC_SEA_SNAKE_CIRCLE_CLOCKWISE: + { + m_circleHeading += m_data[m_type].turnSpeed; + + if ( m_circleHeading > 4096 ) + { + m_circleHeading = 0; + m_turnDir = 0; + } + + m_heading = ( m_origHeading + m_circleHeading ) & 4095; + + s32 preShiftX = _frames * m_speed * rcos( m_heading ); + s32 preShiftY = _frames * m_speed * rsin( m_heading ); + + s32 moveX = preShiftX >> 12; + if ( !moveX && preShiftX ) + { + moveX = preShiftX / abs( preShiftX ); + } + + s32 moveY = preShiftY >> 12; + if ( !moveY && preShiftY ) + { + moveY = preShiftY / abs( preShiftY ); + } + + Pos.vx += moveX; + Pos.vy += moveY; + + break; + } + + case NPC_SEA_SNAKE_CIRCLE_ANTICLOCKWISE: + { + m_circleHeading -= m_data[m_type].turnSpeed; + + if ( m_circleHeading < -4096 ) + { + m_circleHeading = 0; + m_turnDir = 0; + } + + m_heading = ( m_origHeading + m_circleHeading ) & 4095; + + s32 preShiftX = _frames * m_speed * rcos( m_heading ); + s32 preShiftY = _frames * m_speed * rsin( m_heading ); + + s32 moveX = preShiftX >> 12; + if ( !moveX && preShiftX ) + { + moveX = preShiftX / abs( preShiftX ); + } + + s32 moveY = preShiftY >> 12; + if ( !moveY && preShiftY ) + { + moveY = preShiftY / abs( preShiftY ); + } + + Pos.vx += moveX; + Pos.vy += moveY; + + break; + } + + default: + { + DVECTOR waypointPos; + m_npcPath.getCurrentWaypointPos( &waypointPos ); + waypointPos.vy -= 8; + + if ( CGameScene::getCollision()->getHeightFromGround( waypointPos.vx, waypointPos.vy ) < 0 ) + { + // waypoint is either start or end waypoint + + s32 distX, distY; + + distX = waypointPos.vx - Pos.vx; + distY = waypointPos.vy - Pos.vy; + + if( !distX && !distY ) + { + if ( isSnakeStopped() ) + { + m_npcPath.incPath(); + + m_npcPath.getCurrentWaypointPos( &waypointPos ); + waypointPos.vy -= 8; + + if ( CGameScene::getCollision()->getHeightFromGround( waypointPos.vx, waypointPos.vy ) < 0 ) + { + // if next waypoint is ALSO a start/end waypoint, teleport directly to it + + moveEntireSnake( waypointPos ); + oldPos.vx = waypointPos.vx; + oldPos.vy = waypointPos.vy; + + // increment path + m_npcPath.incPath(); + + // point snake in correct direction + m_npcPath.getCurrentWaypointPos( &waypointPos ); + + m_heading = ratan2( waypointPos.vy - Pos.vy, waypointPos.vx - Pos.vx ) & 4095; + } + } + } + else + { + processGenericGotoTarget( _frames, distX, distY, m_speed ); + } + } + else + { + if ( processGenericFixedPathMove( _frames, &moveX, &moveY, &moveVel, &moveDist ) ) + { + // path has changed + + DVECTOR newWaypointPos; + + m_npcPath.getCurrentWaypointPos( &newWaypointPos ); + newWaypointPos.vy -= 8; + + if ( newWaypointPos.vy == waypointPos.vy ) + { + int testDir = newWaypointPos.vx - waypointPos.vx; + + if ( testDir > 0 && testDir <= 16 ) + { + // clockwise + + m_turnDir = NPC_SEA_SNAKE_CIRCLE_CLOCKWISE; + m_circleHeading = 0; + m_origHeading = m_heading; + m_npcPath.incPath(); + } + else if ( testDir < 0 && testDir >= -16 ) + { + // anticlockwise + + m_turnDir = NPC_SEA_SNAKE_CIRCLE_ANTICLOCKWISE; + m_circleHeading = 0; + m_origHeading = m_heading; + m_npcPath.incPath(); + } + } + } + } + + break; + } + } Pos.vx += moveX; Pos.vy += moveY; @@ -751,3 +906,46 @@ s32 CNpcSeaSnakeEnemy::getFrameShift( int _frames ) { return( ( _frames << 8 ) >> 3 ); } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +u8 CNpcSeaSnakeEnemy::isSnakeStopped() +{ + if ( !m_segmentCount ) + { + return( true ); + } + + DVECTOR tailPos = m_segmentArray[m_segmentCount - 1].getPos(); + + if ( tailPos.vx == Pos.vx && tailPos.vy == Pos.vy ) + { + return( true ); + } + else + { + return( false ); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CNpcSeaSnakeEnemy::moveEntireSnake( DVECTOR newPos ) +{ + Pos.vx = newPos.vx; + Pos.vy = newPos.vy; + + int segmentCount; + + for ( segmentCount = 0 ; segmentCount < m_segmentCount ; segmentCount++ ) + { + m_segmentArray[segmentCount].setPos( Pos ); + } + + s16 maxArraySize = NPC_SEA_SNAKE_LENGTH * NPC_SEA_SNAKE_SPACING; + + for ( int histLength = 0 ; histLength < maxArraySize ; histLength++ ) + { + m_positionHistoryArray[histLength].pos = Pos; + } +} diff --git a/source/enemy/nssnake.h b/source/enemy/nssnake.h index c29cc9268..b2c0f2647 100644 --- a/source/enemy/nssnake.h +++ b/source/enemy/nssnake.h @@ -64,11 +64,15 @@ protected: virtual void processShot( int _frames ); virtual void processEnemyCollision( CThing *thisThing ); virtual void processUserCollision( CThing *thisThing ); + u8 isSnakeStopped(); + void moveEntireSnake( DVECTOR newPos ); enum { NPC_SEA_SNAKE_SPACING = 6, NPC_SEA_SNAKE_LENGTH = 10, + NPC_SEA_SNAKE_CIRCLE_CLOCKWISE = 1, + NPC_SEA_SNAKE_CIRCLE_ANTICLOCKWISE = 2, }; // position history stuff @@ -90,6 +94,9 @@ protected: s32 m_snapTimer; //s32 m_openTimer; bool m_meterOn; + u8 m_turnDir; + s16 m_circleHeading; + s16 m_origHeading; }; #endif \ No newline at end of file