/*=========================================================================

	platform.cpp

	Author:		CRB
	Created:
	Project:	Spongebob
	Purpose:

	Copyright (c) 2000 Climax Development Ltd

===========================================================================*/

#include "platform\platform.h"

#ifndef __LEVEL_LEVEL_H__
#include "level\level.h"
#endif

#ifndef __FILE_EQUATES_H__
#include <biglump.h>
#endif

#ifndef __GAME_GAME_H__
#include	"game\game.h"
#endif

#ifndef	__PLAYER_PLAYER_H__
#include	"player\player.h"
#endif

#ifndef __FRIEND_FRIEND_H__
#include "friend\friend.h"
#endif

#ifndef __HAZARD_HAZARD_H__
#include "hazard\hazard.h"
#endif

#ifndef __ENEMY_NPCPATH_H__
#include	"enemy\npcpath.h"
#endif

#ifndef	__UTILS_HEADER__
#include	"utils\utils.h"
#endif

#include "Gfx\actor.h"

#ifndef __VID_HEADER_
#include "system\vid.h"
#endif

#ifndef __PLATFORM_PLINEAR_H__
#include "platform\plinear.h"
#endif

#ifndef __PLATFORM_PCIRCULR_H__
#include "platform\pcirculr.h"
#endif

#ifndef __PLATFORM_PBUBBLE_H__
#include "platform\pbubble.h"
#endif

#ifndef __PLATFORM_PCBUBBLE_H__
#include "platform\pcbubble.h"
#endif

#ifndef __PLATFORM_PFISHHK_H__
#include "platform\pfishhk.h"
#endif

#ifndef __PLATFORM_PRETRACT_H__
#include "platform\pretract.h"
#endif

#ifndef __PLATFORM_PGEYSER_H__
#include "platform\pgeyser.h"
#endif

#ifndef __PLATFORM_PBOB_H__
#include "platform\pbob.h"
#endif

#ifndef __PLATFORM_PFALLING_H__
#include "platform\pfalling.h"
#endif

#ifndef __PLATFORM_PPENDULM_H__
#include "platform\ppendulm.h"
#endif

#ifndef __PLATFORM_PBRANCH_H__
#include "platform\pbranch.h"
#endif

#ifndef __PLATFORM_PCART_H__
#include "platform\pcart.h"
#endif

#ifndef __PLATFORM_PSEESAW_H__
#include "platform\pseesaw.h"
#endif

#ifndef __PLATFORM_PBOUNCE_H__
#include "platform\pbounce.h"
#endif

#ifndef __PLATFORM_PDUAL_H__
#include "platform\pdual.h"
#endif

#ifndef __PLATFORM_PPLAYER_H__
#include "platform\pplayer.h"
#endif

#ifndef __PLATFORM_PFGEN_H__
#include "platform\pfgen.h"
#endif

#ifndef __PLATFORM_PRAFT_H__
#include "platform\praft.h"
#endif

#ifndef __PLATFORM_PLANTERN_H__
#include "platform\plantern.h"
#endif

#ifndef __PLATFORM_PBGEYSER_H__
#include "platform\pbgeyser.h"
#endif

#ifndef __PLATFORM_PLEAF_H__
#include "platform\pleaf.h"
#endif

#ifndef __PLATFORM_PBWHEEL_H__
#include "platform\pbwheel.h"
#endif

#ifndef __PLATFORM_PSBARREL_H__
#include "platform\psbarrel.h"
#endif

#ifndef __PLATFORM_PJELLFSH_H__
#include "platform\pjellfsh.h"
#endif

#ifndef __PLATFORM_PFISHHK3_H__
#include "platform\pfishhk3.h"
#endif

#ifndef __PLATFORM_PRBRIDGE_H__
#include "platform\prbridge.h"
#endif

#ifndef __PLATFORM_PBALOON_H__
#include "platform\pbaloon.h"
#endif

#ifndef __PLATFORM_PTRPDOOR_H__
#include "platform\ptrpdoor.h"
#endif

#ifndef __PLATFORM_PCONVEYR_H__
#include "platform\pconveyr.h"
#endif

#ifndef __PLATFORM_PFALLNOR_H__
#include "platform\pfallnor.h"
#endif

#ifndef __PLATFORM_PDROP_H__
#include "platform\pdrop.h"
#endif

#ifndef __PLATFORM_PSSWITCH_H__
#include "platform\psswitch.h"
#endif

#ifndef __PLATFORM_PLIFT_H__
#include "platform\plift.h"
#endif

#ifndef __PLATFORM_PLURVE_H__
#include "platform\plurve.h"
#endif

#ifndef __PLATFORM_PSOIL_H__
#include "platform\psoil.h"
#endif

#ifndef __PLATFORM_PBUBTUBE_H__
#include "platform\pbubtube.h"
#endif

#ifndef __PLATFORM_PCLAM_H__
#include "platform\pclam.h"
#endif

#ifndef __PLATFORM_PPLAYER_H__
#include "platform\pplayer.h"
#endif

#include "fx\fx.h"
#include "fx\fxjfish.h"


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CNpcPlatform	*CNpcPlatform::Create(int Type)
{
	CNpcPlatform *platform;

	platform = (CNpcPlatform*)CThingManager::GetThing(CThing::TYPE_PLATFORM,Type);
	if (!platform)
	switch( Type )
	{
		case NPC_LINEAR_PLATFORM:
		{
			platform = new ("linear platform") CNpcLinearPlatform;
			break;
		}

		case NPC_CIRCULAR_PLATFORM:
		{
			platform = new ("circular platform") CNpcCircularPlatform;
			break;
		}

		case NPC_BUBBLE_PLATFORM:
		{
			platform = new ("bubble platform") CNpcBubblePlatform;
			break;
		}

		case NPC_COLLAPSING_BUBBLE_PLATFORM:
		{
			platform = new ("collapsing bubble platform") CNpcCollapsingBubblePlatform;
			break;
		}

		case NPC_COLLAPSING_ACRID_PLATFORM:
		{
			platform = new ("collapsing bubble platform") CNpcCollapsingAcridPlatform;
			break;
		}

		case NPC_FISH_HOOK_PLATFORM:
		{
			platform = new ("fish hook platform") CNpcFishHookPlatform;
			break;
		}

		case NPC_RETRACTING_PLATFORM:
		{
			platform = new ("retracting platform") CNpcRetractingPlatform;
			break;
		}

		case NPC_GEYSER_PLATFORM:
		{
			platform = new ("geyser platform") CNpcGeyserPlatform;
			break;
		}

		case NPC_BOBBING_PLATFORM:
		{
			platform = new ("bobbing platform") CNpcBobbingPlatform;
			break;
		}

		case NPC_OILDRUM_PLATFORM:
		case NPC_CRATE_PLATFORM:
		case NPC_FALLING_PLATFORM:
		{
			platform = new ("falling platform") CNpcFallingNoRespawnPlatform;
			break;
		}

		case NPC_FISH_HOOK_2_PLATFORM:
		{
			platform = new ("fish hook 2 platform") CNpcPendulumPlatform;
			break;
		}

		case NPC_BRANCH_PLATFORM:
		{
			platform = new ("branch platform") CNpcBranchPlatform;
			break;
		}

		case NPC_CART_PLATFORM:
		{
			platform = new ("cart platform") CNpcCartPlatform;
			break;
		}

		case NPC_SEESAW_PLATFORM:
		{
			platform = new ("seesaw platform") CNpcSeesawPlatform;
			break;
		}

		case NPC_BOUNCE_PLATFORM:
		{
			platform = new ("bounce platform") CNpcBouncePlatform;
			break;
		}

		case NPC_DUAL_PLATFORM:
		{
			platform = new ("dual platform master") CNpcDualPlatform;
			break;
		}

		case NPC_OILDRUM_GENERATOR:
		{
			CNpcFallingPlatformGenerator *generator;
			generator = new ("oildrum generator") CNpcFallingPlatformGenerator;
			generator->setTargetType( NPC_OILDRUM_PLATFORM );
			platform = generator;
			break;
		}

		case NPC_CRATE_GENERATOR:
		{
			CNpcFallingPlatformGenerator *generator;
			generator = new ("crate generator") CNpcFallingPlatformGenerator;
			generator->setTargetType( NPC_CRATE_PLATFORM );
			platform = generator;
			break;
		}

		case NPC_RAFT_PLATFORM:
		{
			platform = new ("raft platform") CNpcRaftPlatform;
			break;
		}

		case NPC_VERTICAL_OILDRUM_GENERATOR:
		{
			CNpcFallingPlatformGenerator *generator;
			generator = new ("vertical oildrum generator") CNpcFallingPlatformGenerator;
			generator->setTargetType( NPC_VERTICAL_OILDRUM_PLATFORM );
			platform = generator;
			break;
		}

		case NPC_BUBBLE_GEYSER_GENERATOR:
		{
			CNpcGeyserPlatformGenerator *generator;
			generator = new ("bubble geyser generator") CNpcGeyserPlatformGenerator;
			generator->setTargetType( NPC_BUBBLE_PLATFORM );
			platform = generator;
			break;
		}

		case NPC_LANTERN_PLATFORM:
		{
			platform = new ("lantern platform") CNpcLanternPlatform;
			break;
		}

		case NPC_LEAF_PLATFORM:
		{
			platform = new ("leaf platform") CNpcLeafPlatform;
			break;
		}

		case NPC_BIG_WHEEL_PLATFORM:
		{
			platform = new ("big wheel platform") CNpcBigWheelPlatform;
			break;
		}

		case NPC_STEERABLE_BARREL_PLATFORM:
		{
			platform = new ("steerable barrel platform") CNpcSteerableBarrelPlatform;
			break;
		}

		case NPC_JELLYFISH_PLATFORM:
		{
			platform = new ("jellyfish platform") CNpcJellyfishPlatform;
			break;
		}

		case NPC_FISH_HOOK_3_PLATFORM:
		{
			platform = new ("fish hook 3 platform") CNpcFishHook3Platform;
			break;
		}

		case NPC_RISING_BRIDGE_PLATFORM:
		{
			platform = new ("rising bridge platform") CNpcRisingBridgePlatform;
			break;
		}

		case NPC_BALLOON_BRIDGE_PLATFORM:
		{
			platform = new ("balloon bridge platform") CNpcBalloonBridgePlatform;
			break;
		}

		case NPC_TRAPDOOR_PLATFORM:
		{
			platform = new ("trapdoor platform") CNpcTrapdoorPlatform;
			break;
		}

		case NPC_CONVEYOR_GENERATOR:
		{
			platform = new ("conveyor generator") CNpcConveyorPlatformGenerator;
			break;
		}

		case NPC_CONVEYOR_PLATFORM:
		{
			platform = new ("conveyor platform") CNpcConveyorPlatform;
			break;
		}

		case NPC_DROP_PLATFORM:
		{
			platform = new ("drop platform") CNpcDropPlatform;
			break;
		}

		case NPC_STEAM_SWITCH_PLATFORM:
		{
			platform = new ("steam switch platform") CNpcSteamSwitchPlatform;
			break;
		}

		case NPC_LIFT_PLATFORM:
		{
			platform = new ("lift platform") CNpcLiftPlatform;
			break;
		}

		case NPC_LOVE_BOAT_PLATFORM:
		{
			platform = new ("love boat platform") CNpcLoveBoatPlatform;
			break;
		}

		case NPC_STEERABLE_OILDRUM_PLATFORM:
		{
			platform = new ("steerable oildrum platform") CNpcSteerableOildrumPlatform;
			break;
		}

		case NPC_BUBBLE_TUBE_PLATFORM:
		{
			platform = new ("bubble tube platform") CNpcBubbleTubePlatform;
			break;
		}

		case NPC_CLAM_PLATFORM:
		{
			platform = new ("clam platform") CNpcClamPlatform;
			break;
		}

		case NPC_PLAYER_BUBBLE_PLATFORM:
		{
			platform = new ("player bubble platform") CNpcPlayerBubblePlatform;
			break;
		}

		default:
		{
			ASSERT( 0 );
			platform = new ("platform") CNpcPlatform;
			break;
		}
	}

	ASSERT(platform);
	platform->setThingSubType( Type );
	platform->setTiltable( false );

	return( platform );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CNpcPlatform	*CNpcPlatform::Create(sThingPlatform *ThisPlatform)
{
	CNpcPlatform *platform;

	NPC_PLATFORM_UNIT_TYPE Type = getTypeFromMapEdit( ThisPlatform->Type );

	platform=Create(Type);

	switch( Type )
	{
		case NPC_DUAL_PLATFORM:
		{
			CNpcDualPlatform *dualPlatformMaster = (CNpcDualPlatform *) platform;
			dualPlatformMaster->setMaster( true );

			CNpcDualPlatform *dualPlatformSlave;
			dualPlatformSlave = (CNpcDualPlatform *) CNpcPlatform::Create( NPC_DUAL_PLATFORM );
			dualPlatformSlave->setMaster( false );

			dualPlatformSlave->setThingSubType( Type );
			dualPlatformSlave->setGraphic( ThisPlatform );
			dualPlatformSlave->setTiltable( false );

			dualPlatformMaster->setOtherPlatform( dualPlatformSlave );
			dualPlatformSlave->setOtherPlatform( dualPlatformMaster );

			break;
		}

		default:
			break;
	}

	ASSERT(platform);
	platform->setGraphic( ThisPlatform );
	platform->setWaypoints( ThisPlatform );

	return( platform );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setWaypoints( sThingPlatform *ThisPlatform )
{
	int pointNum;

	u16	*PntList=(u16*)MakePtr(ThisPlatform,sizeof(sThingPlatform));
	u16 *waypoints;

	u16 newXPos, newYPos;

	newXPos = (u16) *PntList;
	waypoints = PntList;
	PntList++;
	newYPos = (u16) *PntList;
	PntList++;

	DVECTOR startPos;
	startPos.vx = ( newXPos << 4 ) + 8;
	startPos.vy = ( newYPos << 4 ) + 16;

	init( startPos );

	setWaypointCount( ThisPlatform->PointCount - 1 );

	setWaypointPtr( waypoints );

	s32 minX, maxX, minY, maxY;

	m_npcPath.getPathXExtents( &minX, &maxX );
	m_npcPath.getPathYExtents( &minY, &maxY );

	m_thinkArea.x1 = minX;
	m_thinkArea.x2 = maxX;
	m_thinkArea.y1 = minY;
	m_thinkArea.y2 = maxY;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setGraphic( sThingPlatform *ThisPlatform )
{
	m_graphicNum = ThisPlatform->Gfx;
	m_modelGfx = new ("ModelGfx") CModelGfx;
	m_modelGfx->SetModel( m_graphicNum );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setGraphic( u8 graphicNum )
{
	m_graphicNum = graphicNum;
	m_modelGfx = new ("ModelGfx") CModelGfx;
	m_modelGfx->SetModel( graphicNum );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::init()
{
	CPlatformThing::init();

	m_dataPtr=&m_data[getThingSubType()];

	m_heading = 0;
	m_velocity = 0;
	m_rotation = 0;
	m_reversed = false;
	m_extension = 0;
	m_contact = false;
	m_timer = m_dataPtr->initTimer * GameState::getOneSecondInFrames();
	m_timerType = m_dataPtr->initTimerType;
	m_isActive = true;
	m_detectCollision = m_dataPtr->detectCollision;
	m_state = 0;
	m_tiltAngle = 0;
	m_tiltVelocity = 0;
	m_tiltable = false;
	m_initRotation = 0;

	m_lifetime = 0;
	m_lifetimeType = m_dataPtr->lifetimeType;

	m_npcPath.initPath();

	m_speed = m_dataPtr->speed;

	m_soundId = (int) NOT_PLAYING;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::init( DVECTOR initPos )
{
	init();

	Pos = m_initPos = m_base = initPos;

	m_initLifetime = m_lifetime = GameState::getOneSecondInFrames() * m_dataPtr->lifetime;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::init( DVECTOR initPos, s32 initLifetime )
{
	init( initPos );

	m_initLifetime = m_lifetime = GameState::getOneSecondInFrames() * initLifetime;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::reinit()
{
	m_heading = 0;
	m_velocity = 0;
	m_rotation = 0;
	m_reversed = false;
	m_contact = false;
	m_timer = m_dataPtr->initTimer * GameState::getOneSecondInFrames();
	m_timerType = m_dataPtr->initTimerType;
	m_isActive = true;
	m_detectCollision = m_dataPtr->detectCollision;
	m_state = 0;
	m_tiltAngle = 0;
	m_tiltVelocity = 0;

	m_lifetime = m_initLifetime;

	Pos = m_initPos;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::calculateNonRotatedCollisionData()
{
	DVECTOR collisionSize = getCollisionSize();
	DVECTOR collisionOffset = getCollisionCentreOffset();

	m_nonRotatedCollisionArea.XMax = ( collisionSize.vx >> 1 ) + collisionOffset.vx;
	m_nonRotatedCollisionArea.XMin = -( collisionSize.vx >> 1 ) + collisionOffset.vx;
	m_nonRotatedCollisionArea.YMax = ( collisionSize.vy >> 1 ) + collisionOffset.vy;
	m_nonRotatedCollisionArea.YMin = -( collisionSize.vy >> 1 ) + collisionOffset.vy;

	m_nonRotatedCollisionOffset = collisionOffset;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::postInit()
{
	sBBox boundingBox = m_modelGfx->GetBBox();
	setCollisionSize( ( boundingBox.XMax - boundingBox.XMin ), ( boundingBox.YMax - boundingBox.YMin ) );
	setCollisionCentreOffset( ( boundingBox.XMax + boundingBox.XMin ) >> 1, ( boundingBox.YMax + boundingBox.YMin ) >> 1 );

	calculateNonRotatedCollisionData();
	setCollisionAngle( m_tiltAngle >> 8 );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::shutdown()
{
	if ( m_soundId != NOT_PLAYING )
	{
		CSoundMediator::stopAndUnlockSfx( (xmPlayingId) m_soundId );
	}

	delete m_modelGfx;

	// temporary
	CPlatformThing::shutdown();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::leftThinkZone(int _frames)
{
	if ( m_soundId != NOT_PLAYING )
	{
		CSoundMediator::stopAndUnlockSfx( (xmPlayingId) m_soundId );
		m_soundId = NOT_PLAYING;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::processLifetime( int _frames )
{
	switch( m_lifetimeType )
	{
		case NPC_PLATFORM_FINITE_LIFE:
		{
			m_lifetime -= _frames;

			if ( m_lifetime <= 0 )
			{
				setToShutdown();

				return;
			}

			break;
		}

		case NPC_PLATFORM_FINITE_LIFE_RESPAWN:
		{
			m_lifetime -= _frames;

			if ( m_lifetime <= 0 )
			{
				reinit();
			}

			break;
		}

		case NPC_PLATFORM_INFINITE_LIFE_COLLAPSIBLE:
		{
			if ( m_contact )
			{
				m_lifetime -= _frames;

				if ( m_lifetime <= 0 )
				{
					m_isActive = false;
					m_timer = 3 * GameState::getOneSecondInFrames();
					m_timerType = NPC_PLATFORM_TIMER_RESPAWN;
				}
			}

			break;
		}

		default:
			break;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::think(int _frames)
{
	if ( m_soundId != NOT_PLAYING )
	{
		if( !CSoundMediator::isSfxStillPlaying( (xmPlayingId) m_soundId ) )
		{
			// unlock sound if it has finished

			CSoundMediator::stopAndUnlockSfx( (xmPlayingId) m_soundId );
			m_soundId = NOT_PLAYING;
		}
	}

	if ( m_isActive )
	{
		if ( m_tiltable )
		{
			processTilt( _frames );
		}

		processLifetime( _frames );

		if ( m_heading > 1024 && m_heading < 3072 )
		{
			m_reversed = true;
		}
		else
		{
			m_reversed = false;
		}

		int moveFrames = _frames;

		if ( moveFrames > 2 )
		{
			// make sure platforms don't go berserk if too many frames are dropped

			moveFrames = 2;
		}

		processMovement( moveFrames );

		m_contact = false;
	}

	processTimer( _frames );

	CPlatformThing::think(_frames);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setCollisionAngle(int newAngle)
{
	m_collisionAngle=newAngle;
//	CPlatformThing::setCollisionAngle(newAngle);
	calculateBoundingBoxSize();

	CPlayer	*player;

	// Is the player stood on this platform as it rotates?
	player=GameScene.getPlayer();
	if(player->isOnPlatform()==this)
	{
		DVECTOR	playerPos;

		playerPos=player->getPos();

		DVECTOR shove;
		shove.vx = 0;
		shove.vy = getHeightFromPlatformAtPosition( playerPos.vx, playerPos.vy );
		player->shove(shove);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::calculateBoundingBoxSize()
{
	sBBox boundingBox = m_nonRotatedCollisionArea;

	// 'render' collision box at correct angle

	SVECTOR testPointsNonRel[4];
	VECTOR testPoints[4];

	testPointsNonRel[0].vx = boundingBox.XMin;
	testPointsNonRel[0].vy = boundingBox.YMin;

	testPointsNonRel[1].vx = boundingBox.XMax;
	testPointsNonRel[1].vy = boundingBox.YMin;

	testPointsNonRel[2].vx = boundingBox.XMax;
	testPointsNonRel[2].vy = boundingBox.YMax;

	testPointsNonRel[3].vx = boundingBox.XMin;
	testPointsNonRel[3].vy = boundingBox.YMax;

	MATRIX mtx;
	SetIdentNoTrans(&mtx );
	RotMatrixZ( getCollisionAngle() + m_initRotation, &mtx );

	int i;

	for ( i = 0 ; i < 4 ; i++ )
	{
		ApplyMatrix( &mtx, &testPointsNonRel[i], &testPoints[i] );
	}

	SVECTOR offsetSource;
	VECTOR offsetTarget;

	offsetSource.vx = m_nonRotatedCollisionOffset.vx;
	offsetSource.vy = m_nonRotatedCollisionOffset.vy;

	ApplyMatrix( &mtx, &offsetSource, &offsetTarget );

	int x1, x2, y1, y2;

	x1 = x2 = testPoints[0].vx;
	y1 = y2 = testPoints[0].vy;

	for ( i = 1 ; i < 4 ; i++ )
	{
		if ( testPoints[i].vy < y1 )
		{
			y1 = testPoints[i].vy;
		}
		else if ( testPoints[i].vy > y2 )
		{
			y2 = testPoints[i].vy;
		}

		if ( testPoints[i].vx < x1 )
		{
			x1 = testPoints[i].vx;
		}
		else if ( testPoints[i].vx > x2 )
		{
			x2 = testPoints[i].vx;
		}
	}

	setCollisionSize( x2 - x1 + 1, y2 - y1 + 1 );

	setCollisionCentreOffset( offsetTarget.vx, offsetTarget.vy );
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::processTilt( int _frames )
{
	bool forceActing = false;

	if ( m_contact )
	{
		// user is touching platform, tilt accordingly

		CPlayer *player = GameScene.getPlayer();
		DVECTOR playerPos = player->getPos();

		if ( playerPos.vx > Pos.vx + 10 )
		{
			forceActing = true;

			// tilt clockwise

			if ( m_tiltVelocity < 2560 )
			{
				m_tiltVelocity += 64;
			}
		}
		else if ( playerPos.vx < Pos.vx - 10 )
		{
			forceActing = true;

			// tilt anticlockwise

			if ( m_tiltVelocity > -2560 )
			{
				m_tiltVelocity -= 64;
			}
		}
	}

	if ( !forceActing )
	{
		// no force acting, hence reduce velocity

		s32 reduction = abs( m_tiltVelocity );

		if ( reduction > 64 )
		{
			reduction = 64;
		}

		if ( m_tiltVelocity >= 0 )
		{
			reduction *= -1;
		}

		m_tiltVelocity += reduction;
	}

	m_tiltAngle += m_tiltVelocity;

	if ( m_tiltAngle > ( 512 << 8 ) )
	{
		m_tiltAngle = ( 512 << 8 );
		m_tiltVelocity = 0;
	}
	else if ( m_tiltAngle < -( 512 << 8 ) )
	{
		m_tiltAngle = -( 512 << 8 );
		m_tiltVelocity = 0;
	}

	setCollisionAngle( m_tiltAngle >> 8 );

	/*if ( getCollisionAngle() > 512 && getCollisionAngle() < 3584 )
	{
		m_detectCollision = false;
	}
	else
	{
		m_detectCollision = true;
	}*/
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::processTimer( int _frames )
{
	switch( m_timerType )
	{
		case NPC_PLATFORM_TIMER_NONE:
			break;

		case NPC_PLATFORM_TIMER_RESPAWN:
		{
			if ( m_timer > 0 )
			{
				m_timer -= _frames;
			}
			else
			{
				reinit();
			}

			break;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

u8 CNpcPlatform::checkCollisionDelta( CThing *_thisThing, int threshold, CRECT collisionArea )
{
	// check for case of "was not colliding previously because was above" +
	// "is not colliding now because is below"

	DVECTOR	otherPos = _thisThing->getPos();

	if ( getHeightFromPlatformAtPosition( otherPos.vx, otherPos.vy ) < 0 )
	{
		// is currently below platform landing point

		DVECTOR otherPosDelta = _thisThing->getPosDelta();
		DVECTOR posDelta = getPosDelta();

		if ( otherPosDelta.vy > 0 || posDelta.vy < 0 )
		{
			// object is dropping vertically, or platform is rising vertically

			// get both object's previous positions

			s32 oldXPos = otherPos.vx - otherPosDelta.vx;
			s32 oldYPos = otherPos.vy - otherPosDelta.vy;

			collisionArea.x1 -= posDelta.vx;
			collisionArea.x2 -= posDelta.vx;
			collisionArea.y1 -= posDelta.vy;
			s32 oldCollisionY = collisionArea.y2;
			collisionArea.y2 -= posDelta.vy;

			if ( oldXPos >= collisionArea.x1 && oldXPos <= collisionArea.x2 )
			{
				//if ( oldYPos < collisionArea.y1 + threshold ) //&& otherPos.vy > oldCollisionY )
				if ( getHeightFromPlatformAtPosition( oldXPos, oldYPos, -posDelta.vx, -posDelta.vy ) > 0 )
				{
					// if object's old position was above old platform collision area,
					// but is now below current platform collision area, we can assume
					// the player has hit the platform in the intervening time

					return( true );
				}
			}
		}
	}

	return( false );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::collidedWith( CThing *_thisThing )
{
	switch(_thisThing->getThingType())
	{
		case TYPE_PLAYER:
		{
			CPlayer *player;
			DVECTOR	playerPos;
			CRECT	collisionArea;

			// Only interested in SBs feet colliding with the box (pkg)
			player=(CPlayer*)_thisThing;
			playerPos=player->getPos();
			collisionArea=getCollisionArea();

			s32 threshold = abs( collisionArea.y2 - collisionArea.y1 );

			if ( threshold > 16 )
			{
				threshold = 16;
			}

			if( playerPos.vx >= collisionArea.x1 && playerPos.vx <= collisionArea.x2 )
			{
				if ( checkCollisionDelta( _thisThing, threshold, collisionArea ) )
				{
					player->setPlatform( this );

					m_contact = true;
				}
				else
				{
					if( playerPos.vy >= collisionArea.y1 && playerPos.vy <= collisionArea.y2 )
					{
						int height = getHeightFromPlatformAtPosition( playerPos.vx, playerPos.vy );

						if ( height >= -threshold && height < 1 )
						{
							player->setPlatform( this );

							m_contact = true;
						}
					}
				}
			}

			break;
		}

		case TYPE_NPC:
		{
			CNpcFriend *friendNpc;
			DVECTOR	friendPos;
			CRECT	collisionArea;

			friendNpc = (CNpcFriend*) _thisThing;
			friendPos = friendNpc->getPos();
			collisionArea=getCollisionArea();

			s32 threshold = abs( collisionArea.y2 - collisionArea.y1 );

			if ( threshold > 16 )
			{
				threshold = 16;
			}

			if( friendPos.vx >= collisionArea.x1 && friendPos.vx <= collisionArea.x2 )
			{
				if ( checkCollisionDelta( _thisThing, threshold, collisionArea ) )
				{
					int height = getHeightFromPlatformAtPosition( friendPos.vx, friendPos.vy );

					friendNpc->setPlatform( this );

					m_contact = true;
				}
				else
				{
					if( friendPos.vy >= collisionArea.y1 && friendPos.vy <= collisionArea.y2 )
					{
						int height = getHeightFromPlatformAtPosition( friendPos.vx, friendPos.vy );

						if ( height >= -threshold && height < 1 )
						{
							friendNpc->setPlatform( this );

							m_contact = true;
						}
					}
				}
			}

			break;
		}

		case TYPE_HAZARD:
			break;

		default:
			ASSERT(0);
			break;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::processMovement( int _frames )
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::render()
{
	if ( m_isActive )
	{
		CPlatformThing::render();

		// Render
		if (canRender())
		{
			DVECTOR &renderPos=getRenderPos();

			m_modelGfx->Render(renderPos);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CNpcPlatform::checkCollisionAgainst(CThing *_thisThing, int _frames)
{
	switch(_thisThing->getThingType())
	{
		case TYPE_PLAYERPROJECTILE:
			return( false );

		default:
		{
			int collided = false;

			if ( m_detectCollision && m_isActive && !isSetToShutdown() )
			{
				CRECT thisRect, thatRect;

				thisRect = getCollisionArea();
				thatRect = _thisThing->getCollisionArea();

				DVECTOR posDelta = getPosDelta();

				thisRect.y1 -= abs( posDelta.vy ) >> 1;
				thisRect.y2 += abs( posDelta.vy ) >> 1;

				posDelta = _thisThing->getPosDelta();

				thatRect.y1 -= abs( posDelta.vy ) >> 1;
				thatRect.y2 += abs( posDelta.vy ) >> 1;

				if(((thisRect.x1>=thatRect.x1&&thisRect.x1<=thatRect.x2)||(thisRect.x2>=thatRect.x1&&thisRect.x2<=thatRect.x2)||(thisRect.x1<=thatRect.x1&&thisRect.x2>=thatRect.x2))&&
				   ((thisRect.y1>=thatRect.y1&&thisRect.y1<=thatRect.y2)||(thisRect.y2>=thatRect.y1&&thisRect.y2<=thatRect.y2)||(thisRect.y1<=thatRect.y1&&thisRect.y2>=thatRect.y2)))
				{
					collided = true;
				}
			}

			return( collided );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setTiltable( bool isTiltable )
{
	m_tiltable = isTiltable;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int	CNpcPlatform::getHeightFromPlatformAtPosition(int _x,int _y, int offsetX, int offsetY)
{
	DVECTOR top;
	int		angle;

	CRECT collisionArea = getCollisionArea();

	top.vy = offsetY + collisionArea.y1;

	angle=getCollisionAngle() + m_initRotation;
	if(angle==0)
	{
		// Non-rotated platform

		return( top.vy - _y );
	}
	else
	{
		if ( angle > 0 )
		{
			top.vx = offsetX + collisionArea.x1;
		}
		else
		{
			top.vx = offsetX + collisionArea.x2;
		}

		// Rotate backwards to find height at current position

		s16 cosVal = rcos( angle );

		if ( !cosVal )
		{
			return( top.vy - _y );
		}
		else
		{
			int hypotenuse = ( ( top.vx - _x ) << 12 ) / cosVal;

			int angleHeight = -( hypotenuse * rsin( angle ) ) >> 12;

			return( ( top.vy - _y ) + angleHeight );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*void CNpcPlatform::addWaypoint( u16 *ptr )
{
	m_npcPath.addWaypoint( ptr );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::addWaypoint( u16 *ptrX, u16 *ptrY )
{
	m_npcPath.addWaypoint( ptrX, ptrY );
}*/

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CNpcPlatform::setTypeFromMapEdit( u16 newType )
{
int	Type= mapEditConvertTable[newType];
	setThingSubType(Type);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CNpcPlatform::NPC_PLATFORM_UNIT_TYPE CNpcPlatform::getTypeFromMapEdit( u16 newType )
{
	return( mapEditConvertTable[newType] );
}