#include #include #include #include #include #include #include #include #include "LogClient/LogClient.h" #include "ErrorCode/ErrorCode.h" #include "ScriptNPC.h" #include "StructPlayer.h" #include "StructMonster.h" #include "StructSummon.h" #include "StructNPC.h" #include "StructSkill.h" #include "InstanceDungeonManager.h" #include "FieldPropManager.h" #include "SendMessage.h" #include "NPCProc.h" #include "GameMessage.h" #include "GameProc.h" #include "ScriptCommon.h" #include "GameContent.h" #include "DungeonManager.h" #include "ChannelManager.h" #include "RoamingManager.h" #include extern XCriticalSection g_Lock; extern std::vector< StructNPC* > g_vNPC; extern std::vector< StructSummon* > g_vSummon; int SCRIPT_GetString( struct lua_State *L ) { if( lua_gettop(L) < 1 ) return 0; const char *szString = getStringResource( lua_tostring_utf8( L, lua_gettop(L) ).c_str() ); lua_pushstring_utf8( L, szString ); return 1; } int SCRIPT_AddMonster( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddMonster() : invalid argument" ); return 1; } int x = (int)lua_tonumber( L, 1 ); int y = (int)lua_tonumber( L, 2 ); int code = (int)lua_tonumber( L, 3 ); unsigned char layer = 0; int count = 1; AR_TIME life_time = 0; StructPlayer::iterator pit; StructPlayer * pPlayer = NULL; if( lua_gettop( L ) > 5 && lua_isnumber( L, 6 ) && lua_isnumber( L, 5 ) && lua_isnumber( L, 4 ) ) { count = lua_tonumber( L, 4 ); life_time = lua_tonumber( L, 5 ); layer = lua_tonumber( L, 6 ); } else if( lua_gettop( L ) > 4 && lua_isnumber( L, 5 ) && lua_isnumber( L, 4 ) ) { count = lua_tonumber( L, 4 ); life_time = lua_tonumber( L, 5 ); pit = getPlayer( L, 6 ); pPlayer = *pit; } else if( lua_gettop( L ) > 3 && lua_isnumber( L, 4 ) ) { count = lua_tonumber( L, 4 ); pit = getPlayer( L, 5 ); pPlayer = *pit; } else { pit = getPlayer( L, 4 ); pPlayer = *pit; } if( !life_time ) life_time = INFINITE_TIME; if( pPlayer && n < 6 ) { layer = pPlayer->GetLayer(); } std::vector< GameObject * > vObject; std::vector< AR_HANDLE > vHandle; int nInstanceDungeonID = InstanceDungeonManager::Instance().GetInstanceDungeonID( ArPosition( x, y ) ); if( nInstanceDungeonID ) { if( !pPlayer && n < 6 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddMonster() : script calling without specified layer" ); return 1; } // 인던에 몬스터를 생성하려고 하는 경우 리스폰 프로세스를 인던 매니저에게 역임 InstanceDungeonManager::Instance().PendRespawnMonster( nInstanceDungeonID, x, y, layer, code, count, life_time, NULL ); // 인던에 소환했을 경우 소환된 몬스터 핸들이 아직 없으므로 0 리턴 lua_pushnumber( L, 0 ); } else { for( int i = 0; i < count; ++i ) { // 몬스터 생성 StructMonster *pMob = StructMonster::AllocMonster( code ); if( !pMob ) { return 0; } // 생성코드 설정 pMob->SetGenerateCode( StructMonster::BY_SCRIPT ); // 좌표 설정 pMob->SetCurrentXY( x, y ); pMob->SetCurrentLayer( layer ); pMob->SetLifeTime( life_time ); // 지역 락이 중첩해서 걸리는 문제를 방지하기 위해 월드에 등장시키는 걸 ObjectRespawner에게 일임 vObject.push_back( pMob ); vHandle.push_back( pMob->GetHandle() ); } ObjectRespawner * pRespawner = new ObjectRespawner( vObject ); ArcadiaServer::Instance().SetObjectPriority( pRespawner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); lua_newtable( L ); for( int i = 1; i <= count; i++ ) { lua_pushnumber( L, i ); lua_pushnumber( L, vHandle[i-1] ); lua_rawset( L, -3 ); } lua_pushstring( L, "n" ); lua_pushnumber( L, count ); lua_rawset( L, -3 ); } return 1; } int SCRIPT_AddInstanceDungeonMonster( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddInstanceDungeonMonster() : invalid argument" ); return 1; } int nRespawnID = (int)lua_tonumber( L, 1 ); int nInstanceDungeonID = (int)lua_tonumber( L, 2 ); int nLayer = (int)lua_tonumber( L, 3 ); // 인던에 몬스터를 생성하려고 하는 경우 리스폰 프로세스를 인던 매니저에게 역임 if( !InstanceDungeonManager::Instance().PendRespawnMonster( nRespawnID, nInstanceDungeonID, nLayer ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddInstanceDungeonMonster() : failed to regenerate a monster group" ); return 1; } return 0; } int SCRIPT_AddNPC( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) ) { LUA()->Log( "SCRIPT_AddNPC() : invalid argument" ); lua_pushnumber( L, 1 ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer * pPlayer = *pit; int nX = lua_tonumber( L, 1 ); int nY = lua_tonumber( L, 2 ); unsigned char nLayer = ( nArgCnt > 3 ) ? lua_tonumber( L, 3 ) : ( ( pPlayer ) ? pPlayer->GetLayer() : 0 ); int nNPCID = lua_tonumber( L, nArgCnt ); NPCBase & info( GameContent::GetNPCInfo( nNPCID ) ); if( !info.id ) { lua_pushnumber( L, 2 ); return 1; } StructNPC * pNPC = GameContent::GetNewNPC( info, nLayer ); pNPC->SetCurrentXY( nX, nY ); pNPC->SetHP( pNPC->GetMaxHP() ); pNPC->SetStatus( StructNPC::STATUS_NORMAL ); // 지역 락이 중첩해서 걸리는 문제를 방지하기 위해 월드에 등장시키는 걸 ObjectRespawner에게 일임 ObjectRespawner * pRespawner = new ObjectRespawner( pNPC ); ArcadiaServer::Instance().SetObjectPriority( pRespawner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); lua_pushnumber( L, 0 ); return 1; } typedef std::pair< StructFieldProp *, AR_TIME > PropExpirationInfo; // 아래의 SCRIPT_AddFieldProp에서 리젠시킨 필드 프랍의 등장 제한 시간 경과 처리 및 삭제 처리 struct RespawnedFieldPropManager : public ArSchedulerObject, StructFieldProp::FieldPropDeleteHandler { static RespawnedFieldPropManager & Instance() { static RespawnedFieldPropManager _instance; return _instance; } void AddFieldPropExpirationInfo( struct StructFieldProp * pProp, const AR_TIME nExpirationTime ) { THREAD_SYNCHRONIZE( m_CS ); m_vPropExpirationList.push_back( std::make_pair( pProp, nExpirationTime ) ); if( GetFinalPriority() != UPDATE_PRIORITY_HIGH ) ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_HIGH ); } virtual void onFieldPropDelete( struct StructFieldProp * pProp ) { { THREAD_SYNCHRONIZE( m_CS ); for( std::vector< PropExpirationInfo >::iterator it = m_vPropExpirationList.begin() ; it != m_vPropExpirationList.end() ; ++it ) { if( (*it).first != pProp ) continue; m_vPropExpirationList.erase( it ); break; } if( m_vPropExpirationList.empty() && GetFinalPriority() != UPDATE_PRIORITY_IDLE ) ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_IDLE ); } const GameContent::FIELD_PROP_RESPAWN_INFO * pInfo = pProp->GetRespawnInfo(); if( pInfo ) delete pInfo; } virtual void onProcess( int nThreadIdx ) { char buf[255]; s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx ); ENV().Set( buf, "RespawnedFieldPropManager" ); AR_TIME tCurrent = GetArTime(); extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo; s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "RespawnedFieldPropManager(0x%08X)", (UINT_PTR)this ); s_ThreadInfo.last_execute_time = tCurrent; // 지역락 -> m_CS의 순서를 어기지 않도록 만료 처리되어야 할 프랍 정보를 복사해놓고 처리 std::vector< StructFieldProp * > vExpiredPropList; { THREAD_SYNCHRONIZE( m_CS ); for( std::vector< PropExpirationInfo >::iterator it = m_vPropExpirationList.begin() ; it != m_vPropExpirationList.end() ; /* 루프에서 ++it 처리 */ ) { if( (*it).second > tCurrent ) { ++it; continue; } vExpiredPropList.push_back( (*it).first ); it = m_vPropExpirationList.erase( it ); } if( m_vPropExpirationList.empty() && GetFinalPriority() != UPDATE_PRIORITY_IDLE ) ArcadiaServer::Instance().SetObjectPriority( this, UPDATE_PRIORITY_IDLE ); } for( std::vector< StructFieldProp * >::const_iterator it = vExpiredPropList.begin() ; it != vExpiredPropList.end() ; ++it ) { StructFieldProp * pProp = (*it); // 다른 곳에서 동시에 삭제 처리가 되고 있을 수도 있으므로 실제로 삭제 처리가 여기서 성공한 이후에 delete를 하도록 미리 포인터만 보관 const GameContent::FIELD_PROP_RESPAWN_INFO * pInfo = pProp->GetRespawnInfo(); { ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pProp ) ); // 지역 락 건 이후에 어디선가 사용한다거나 해서 삭제 처리되었을 경우 건너 뛰기 if( !pProp->IsInWorld() || !pProp->IsEnable() ) continue; // m_vPropExpirationList에서 이미 삭제된 상태이므로 DeleteHandler를 해제함 pProp->SetDeleteHandler( NULL ); ArcadiaServer::Instance().RemoveObject( pProp ); StructFieldProp::PendFreeFieldProp( pProp ); } if( pInfo ) delete pInfo; } } protected: RespawnedFieldPropManager() : m_CS( "RespawnedFieldPropManager::m_CS" ) {} // m_vPropExpirationList를 보호하며 지역락 -> m_CS 의 순서로 걸어야 함 mutable XCriticalSection m_CS; std::vector< PropExpirationInfo > m_vPropExpirationList; }; int SCRIPT_AddFieldProp( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) ) { LUA()->Log( "SCRIPT_AddFieldProp() : invalid argument" ); lua_pushnumber( L, 0 ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer * pPlayer = (*pit); if( !pPlayer && ( nArgCnt < 5 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) ) ) { LUA()->Log( "SCRIPT_AddFieldProp() : invalid argument" ); lua_pushnumber( L, 0 ); return 1; } int nPropId = lua_tonumber( L, 1 ); if( !nPropId || FieldPropManager::GetInstance().GetFieldPropBase( nPropId ).nPropId != nPropId ) { LUA()->Log( "SCRIPT_AddFieldProp() : invalid prop id" ); lua_pushnumber( L, 0 ); return 1; } AR_TIME nValidDuration = lua_tonumber( L, 2 ) * 100; AR_UNIT nPosX = ( nArgCnt >= 3 && lua_isnumber( L, 3 ) ) ? lua_tonumber( L, 3 ) : pPlayer->GetX(); AR_UNIT nPosY = ( nArgCnt >= 4 && lua_isnumber( L, 4 ) ) ? lua_tonumber( L, 4 ) : pPlayer->GetY(); unsigned char nLayer = ( nArgCnt >= 5 && lua_isnumber( L, 5 ) ) ? lua_tonumber( L, 5 ) : pPlayer->GetLayer(); float fZOffset = ( nArgCnt >= 6 && lua_isnumber( L, 6 ) ) ? lua_tonumber( L, 6 ) : 0.0f; float fRotateX = ( nArgCnt >= 7 && lua_isnumber( L, 7 ) ) ? lua_tonumber( L, 7 ) : 0.0f; float fRotateY = ( nArgCnt >= 8 && lua_isnumber( L, 8 ) ) ? lua_tonumber( L, 8 ) : 0.0f; float fRotateZ = ( nArgCnt >= 9 && lua_isnumber( L, 9 ) ) ? lua_tonumber( L, 9 ) : 0.0f; float fScaleX = ( nArgCnt >= 10 && lua_isnumber( L, 10 ) ) ? lua_tonumber( L, 10 ) : 1.0f; float fScaleY = ( nArgCnt >= 11 && lua_isnumber( L, 11 ) ) ? lua_tonumber( L, 11 ) : 1.0f; float fScaleZ = ( nArgCnt >= 12 && lua_isnumber( L, 12 ) ) ? lua_tonumber( L, 12 ) : 1.0f; bool bLockHeight = ( nArgCnt >= 13 && lua_isboolean( L, 13 ) ) ? lua_toboolean( L, 13 ) : false; float fLockHeight = ( nArgCnt >= 14 && lua_isnumber( L, 14 ) ) ? lua_tonumber( L, 14 ) : 0.0f; ArPosition pos = ArPosition( nPosX, nPosY ); int nInstanceDungeonID = InstanceDungeonManager::Instance().GetInstanceDungeonID( pos ); AR_HANDLE nHandle = -1; if( nInstanceDungeonID ) { InstanceDungeonManager::Instance().PendRespawnProp( nInstanceDungeonID, nPropId, nPosX, nPosY, nLayer, fZOffset, fRotateX, fRotateY, fRotateZ, fScaleX, fScaleY, fScaleZ, bLockHeight, fLockHeight ); } else { GameContent::FIELD_PROP_RESPAWN_INFO * pInfo = new GameContent::FIELD_PROP_RESPAWN_INFO( nPropId, nPosX, nPosY, nLayer, fZOffset, fRotateX, fRotateY, fRotateZ, fScaleX, fScaleY, fScaleZ, bLockHeight, fLockHeight ); StructFieldProp * pProp = StructFieldProp::CreateWithoutAddingToWorld( &RespawnedFieldPropManager::Instance(), pInfo ); if( nValidDuration || pProp->IsExpireObject() ) { RespawnedFieldPropManager::Instance().AddFieldPropExpirationInfo( pProp, GetArTime() + ( ( nValidDuration ) ? nValidDuration : pProp->GetFieldPropBase()->nLifeTime ) ); } // 지역 락이 중첩해서 걸리는 문제를 방지하기 위해 월드에 등장시키는 걸 ObjectRespawner에게 일임 ObjectRespawner * pRespawner = new ObjectRespawner( pProp ); ArcadiaServer::Instance().SetObjectPriority( pRespawner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); nHandle = pProp->GetHandle(); } lua_pushnumber( L, nHandle ); return 1; } int SCRIPT_AddDungeonGuardianRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 8 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) || !lua_isnumber(L, 8) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddDungeonGardianRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 8 && lua_isnumber( L, 9 ) ) { way_point_id = lua_tonumber( L, 9 ); } DungeonManager::Instance().RegisterDungeonGuardianRespawnInfo( (int)lua_tonumber( L, 1 ), GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), (int)lua_tonumber( L, 7 ), (bool)lua_tonumber( L, 8 ), way_point_id ) ); return 0; } int SCRIPT_AddDungeonEnvironmentalGuardianRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 8 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) || !lua_isnumber(L, 8) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddDungeonEnvironmentalGuardianRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 8 && lua_isnumber( L, 9 ) ) { way_point_id = lua_tonumber( L, 9 ); } DungeonManager::Instance().RegisterDungeonEnvironmentalGuardianRespawnInfo( (int)lua_tonumber( L, 1 ), GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), (int)lua_tonumber( L, 7 ), (bool)lua_tonumber( L, 8 ), way_point_id ) ); GameContent::RegisterMonsterRespawnInfo( GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), (int)lua_tonumber( L, 7 ), (bool)lua_tonumber( L, 8 ), way_point_id ) ); return 0; } int SCRIPT_AddRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 9 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) || !lua_isnumber(L, 8) || !lua_isnumber(L, 9) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 9 && lua_isnumber( L, 10 ) ) { way_point_id = lua_tonumber( L, 10 ); } GameContent::RegisterMonsterRespawnInfo( GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), (int)lua_tonumber( L, 8 ), (int)lua_tonumber( L, 9 ), true, way_point_id ) ); return 0; } int SCRIPT_AddRareMobRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 7 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRareMobRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 7 && lua_isnumber( L, 8 ) ) { way_point_id = lua_tonumber( L, 8 ); } GameContent::RegisterMonsterRespawnInfo( GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 3 ) + 1, (int)lua_tonumber( L, 4 ) + 1, (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 6 ), (bool)lua_tonumber( L, 7 ), way_point_id ) ); return 0; } int SCRIPT_AddRoamingMobRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) || ( n >= 5 && !lua_isnumber( L, 5 ) ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRoamingMobRespawnInfo() : invalid argument" ); return 1; } int nRoamingID = lua_tonumber( L, 1 ); int nMonsterID = lua_tonumber( L, 2 ); int nAngle = lua_tonumber( L, 3 ); AR_UNIT nDistance = lua_tonumber( L, 4 ) * GameRule::DEFAULT_UNIT_SIZE; AR_TIME nRespawnInterval = ( n >= 5 ) ? lua_tonumber( L, 5 ) * 100 : 0; // 리젠 그룹화 로밍 그룹이 아닌 경우 인자가 4개거나 5번째 인자가 0이면 1번 리젠하고 삭제함 RoamingManager::Instance().AddRoamingCreatureRespawnInfo( nRoamingID, GameContent::ROAMING_CREATURE_RESPAWN_INFO( StructRoamer::ROAMING_CREATURE_MONSTER, nMonsterID, nRespawnInterval, nAngle, nDistance ) ); return 0; } int SCRIPT_AddRaidRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 9 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) || !lua_isnumber(L, 8) || !lua_isnumber(L, 9) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 9 && lua_isnumber( L, 10 ) ) { way_point_id = lua_tonumber( L, 10 ); } GameContent::RegisterRaidMonsterRespawnInfo( GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), (int)lua_tonumber( L, 8 ), (int)lua_tonumber( L, 9 ), true, way_point_id ) ); return 0; } int SCRIPT_AddRaidRareMobRespawnInfo( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 7 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRespawnInfo() : invalid argument" ); return 1; } int way_point_id = 0; if( n > 7 && lua_isnumber( L, 8 ) ) { way_point_id = lua_tonumber( L, 8 ); } GameContent::RegisterRaidMonsterRespawnInfo( GameContent::MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (int)lua_tonumber( L, 3 ) + 1, (int)lua_tonumber( L, 4 ) + 1, (int)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 6 ), (bool)lua_tonumber( L, 7 ), way_point_id ) ); return 0; } int SCRIPT_SetRandomRespawnInfo( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments bool bExceptRaidSiege = false; if( n < 7 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6) || !lua_isnumber(L, 7) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetRandomRespawnInfo() : invalid argument" ); return 1; } if( n > 7 && lua_isnumber( L, 8 ) ) { bExceptRaidSiege = lua_tonumber( L, 8 ); } GameContent::RegisterRandomMonsterRespawnInfo( GameContent::RANDOM_MONSTER_RESPAWN_INFO( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ), (int)lua_tonumber( L, 4 ), (bool)lua_tonumber( L, 5 ), (int)lua_tonumber( L, 6 ), (int)lua_tonumber( L, 7 ), bExceptRaidSiege ) ); return 0; } int SCRIPT_AddRandomAreaInfo( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 5 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRandomAreaInfo() : invalid argument" ); return 1; } GameContent::AddRandomAreaInfo( lua_tonumber( L, 1 ), lua_tonumber( L, 2 ), lua_tonumber( L, 3 ), lua_tonumber( L, 4), lua_tonumber( L, 5 ) ); return 0; } int SCRIPT_AddRandomMonster( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRandomMonster() : invalid argument" ); return 1; } GameContent::AddRandomMonster( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ) ); return 0; } int SCRIPT_AddRandomMonsterGroup( struct lua_State *L ) { int n = lua_gettop( L ); if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddRandomMonsterGroup() : invalid argument" ); return 1; } GameContent::AddRandomMonsterGroup( (int)lua_tonumber( L, 1 ), (int)lua_tonumber( L, 2 ), (int)lua_tonumber( L, 3 ) ); return 0; } int SCRIPT_DialogTitle( struct lua_State *L ) { if( lua_gettop(L) < 1 ) return 0; StructPlayer::iterator pit = getPlayer( L, 2 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; pPlayer->SetDialogTitle( lua_tostring_utf8( L, 1 ).c_str() ); return 0; } typedef std::pair< int /* QuestCode */, int /* TextID */ > QuestLinkInfo; int ShowQuestMenu( StructPlayer *pPlayer ) { StructNPC * pNPC = reinterpret_cast< StructNPC * > (GameObject::raw_get( pPlayer->GetContactNPCHandle() ) ); if( !pNPC ) return 0; struct myQuestFunctor : public StructNPC::QuestFunctor { myQuestFunctor( int progress ) : m_QuestProgress( progress ) { } bool operator()( struct StructPlayer * pPlayer, const QuestLink &link_info ) { const QuestBase & rQuestBase = StructQuest::GetQuestBase( link_info.code ); int nTextID = 0; switch( m_QuestProgress ) { case TS_SC_QUEST_INFOMATION::IS_STARTABLE: nTextID = link_info.nStartTextId; break; case TS_SC_QUEST_INFOMATION::IS_IN_PROGRESS: nTextID = link_info.nInProgressTextId; break; case TS_SC_QUEST_INFOMATION::IS_FINISHABLE: nTextID = link_info.nEndTextId; break; } // QuestLinkResource에 퀘스트의 현재 진행 단계에 대한 flag는 설정되어 있지만 text_id는 0인 경우 if( !nTextID ) { assert( 0 ); return true; } char szBuf[255]; if( rQuestBase.nType == QuestBase::QUEST_RANDOM_KILL_INDIVIDUAL || rQuestBase.nType == QuestBase::QUEST_RANDOM_COLLECT ) { vRandomQuestInfo.push_back( QuestLinkInfo( link_info.code, nTextID ) ); // 랜덤 퀘스트의 시작 메뉴는 아래에서 별도로 처리하며 진행 중이거나 완료 가능한 것만 메뉴를 추가함 if( m_QuestProgress == TS_SC_QUEST_INFOMATION::IS_STARTABLE ) { return true; } // 진행 중 또는 완료 가능 랜덤퀘는 NPC 대사로 해당 다이얼로그가 출력되도록 강제로 설정함 s_sprintf( szBuf, _countof( szBuf ), "quest_info( %d, %d, 0 )", link_info.code, nTextID ); } else { // 일반 퀘는 진행 상태에 따라 창의 타입이 자동 결정되도록 처리(SCRIPT_QuestInfo 함수 구현 참조) s_sprintf( szBuf, _countof( szBuf ), "quest_info( %d, %d )", link_info.code, nTextID ); } char szButtonName[126]; s_sprintf( szButtonName, _countof( szButtonName ), "QUEST|%d|%d", rQuestBase.nQuestTextId, m_QuestProgress ); pPlayer->AddDialogMenu( szButtonName, szBuf ); return true; } int m_QuestProgress; std::vector< QuestLinkInfo > vRandomQuestInfo; }; myQuestFunctor _foStart( TS_SC_QUEST_INFOMATION::IS_STARTABLE ); myQuestFunctor _foProgress( TS_SC_QUEST_INFOMATION::IS_IN_PROGRESS ); myQuestFunctor _foEnd( TS_SC_QUEST_INFOMATION::IS_FINISHABLE ); pNPC->DoEachStartableQuest( pPlayer, _foStart ); pNPC->DoEachInProgressQuest( pPlayer, _foProgress ); pNPC->DoEachFinishableQuest( pPlayer, _foEnd ); // 진행 중이거나 완료 가능한 랜덤 퀘스트가 없다면 새로 시작할 수 있도록 퀘스트 메뉴 추가 if( !_foProgress.vRandomQuestInfo.size() && !_foEnd.vRandomQuestInfo.size() ) { int max_level = 0; QuestLinkInfo qli( 0, 0 ); // 현재 시작할 수 있는 랜덤 퀘스트 중 가장 제한 레벨이 높은 퀘스트를 골라 보여 줌 for( std::vector< QuestLinkInfo >::iterator it = _foStart.vRandomQuestInfo.begin(); it != _foStart.vRandomQuestInfo.end(); ++it ) { const int & nLimitLevel = StructQuest::GetQuestBase( (*it).first ).nLimitLevel; if( nLimitLevel > max_level ) { qli = (*it); max_level = nLimitLevel; } } if( qli.first ) { const QuestBase & rQuestBase = StructQuest::GetQuestBase( qli.first ); // 랜덤 퀘스트의 경우 NPC 대사로 해당 다이얼로그가 출력되도록 강제로 설정함 char szBuf[255]; s_sprintf( szBuf, _countof( szBuf ), "quest_info( %d, %d, 0 )", qli.first, qli.second ); char szButtonName[126]; s_sprintf( szButtonName, _countof( szButtonName ), "QUEST|%d|%d", rQuestBase.nQuestTextId, TS_SC_QUEST_INFOMATION::IS_STARTABLE ); pPlayer->AddDialogMenu( szButtonName, szBuf ); } } return 0; } int SCRIPT_DialogText( struct lua_State *L ) { if( lua_gettop(L) < 1 ) return 0; StructPlayer::iterator pit = getPlayer( L, 2 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; pPlayer->SetDialogText( lua_tostring_utf8( L, 1 ).c_str() ); ShowQuestMenu( pPlayer ); return 0; } int SCRIPT_QuestTextWithoutQuest( struct lua_State *L ) { if( lua_gettop(L) < 2 ) return 0; char buf[255]; StructPlayer::iterator pit = getPlayer( L, 3 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; int code = lua_tonumber( L, 1 ); int text_id = lua_tonumber( L, 2 ); s_sprintf( buf, _countof( buf ), "QUEST|%d|%d", code, text_id ); pPlayer->SetDialogText( buf ); return 0; } int SCRIPT_DialogTextWithoutQuest( struct lua_State *L ) { if( lua_gettop(L) < 1 ) return 0; StructPlayer::iterator pit = getPlayer( L, 2 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; pPlayer->SetDialogText( lua_tostring_utf8( L, 1 ).c_str() ); return 0; } int SCRIPT_DialogMenu( struct lua_State *L ) { if( lua_gettop(L) < 2 ) return 0; StructPlayer::iterator pit = getPlayer( L, 3 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; pPlayer->AddDialogMenu( lua_tostring_utf8( L, 1 ).c_str(), lua_tostring_utf8( L, 2 ).c_str() ); return 0; } int SCRIPT_DialogShow( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; pPlayer->ShowDialog(); return 0; } int SCRIPT_ShowMarket( struct lua_State *L ) { // 상점 이름 안주면 KIN if( lua_gettop(L) < 1 ) return 0; StructPlayer::iterator pit = getPlayer( L, 2 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; // 상점 얻어오자 _MARKET_INFO *pInfo = NULL; pInfo = GameContent::GetMarketInfo( lua_tostring_utf8( L, 1 ).c_str() ); if( !pInfo ) return 0; SendMarketInfo( pPlayer, pPlayer->GetContactNPCHandle(), pInfo ); return 0; } int SCRIPT_ShowCreatureDialog( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; SendCreatureEquipMessage( pPlayer, true ); return 0; } int SCRIPT_ShowStorage( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1); StructPlayer *pPlayer = *pit; if( !pPlayer || !pPlayer->pConnection || !pPlayer->pConnection->GetTag() ) return 0; _CONNECTION_TAG* pTag = static_cast<_CONNECTION_TAG*>( pPlayer->pConnection->GetTag() ); // 보안 비밀번호를 쓰는 옵션이 설정되어 있고 // 항상 창고비밀번호 확인 옵션이 설정되어 있거나, // 창고비밀번호 확인이 한번도 안된경우, 비밀번호 확인해라!!! if ( GameRule::bUseSecurityNoForStorage && ( GameRule::bCheckStorageSecurityAlways || !pTag->bStorageSecurityCheck ) ) pPlayer->RequestSecurityNo( TS_SC_REQUEST_SECURITY_NO::SC_OPEN_STORAGE ); else pPlayer->OpenStorage(); return 0; } int SCRIPT_GetNPCId( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; if( pPlayer->GetContactNPCHandle() ) { StructCreature::iterator it = StructCreature::get( pPlayer->GetContactNPCHandle() ); if( *it && (*it)->IsNPC() ) { lua_pushnumber( L, static_cast< StructNPC * >( *it )->GetNPCID() ); return 1; } } lua_pushnumber( L, 0 ); return 1; } int SCRIPT_GetNPCHandle( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; if( pPlayer->GetContactNPCHandle() ) { StructCreature::iterator it = StructCreature::get( pPlayer->GetContactNPCHandle() ); if( *it && (*it)->IsNPC() ) { lua_pushnumber( L, static_cast< StructNPC * >( *it )->GetHandle() ); return 1; } } lua_pushnumber( L, 0 ); return 1; } int SCRIPT_GetNPCType( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; if( pPlayer->GetContactNPCHandle() ) { StructCreature::iterator it = StructCreature::get( pPlayer->GetContactNPCHandle() ); if( *it && (*it)->IsNPC() ) { lua_pushnumber( L, static_cast< StructNPC * >( *it )->GetNPCType() ); return 1; } } lua_pushnumber( L, 0 ); return 1; } int SCRIPT_GetNPCName( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; if( pPlayer->GetContactNPCHandle() ) { StructCreature::iterator it = StructCreature::get( pPlayer->GetContactNPCHandle() ); if( *it && (*it)->IsNPC() ) { lua_pushnumber( L, static_cast< StructNPC * >( *it )->GetNPCName() ); return 1; } } lua_pushnumber( L, 0 ); return 1; } int SCRIPT_MonsterSkillCast( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 2 || n > 4 ) { LUA()->Log( "SCRIPT_MonsterSkillCast() : invalid argument" ); return 0; } int skill_idx = lua_tonumber( L, 1 ); AR_HANDLE hMonster = lua_tonumber( L, 2 ); AR_HANDLE hTarget = 0; ArPosition posTarget; StructMonster::iterator itMonster = StructMonster::get( hMonster ); if( !(*itMonster) || !(*itMonster)->IsMonster() || !(*itMonster)->IsAlive() ) return 0; StructMonster * pMonster = static_cast< StructMonster * >( *itMonster ); if( n == 3 && lua_isnumber( L, 3 ) ) { hTarget = lua_tonumber( L, 3 ); StructMonster::iterator itTarget = StructMonster::get( hTarget ); if( !(*itTarget) || !(*itTarget)->IsCreature() || !(*itTarget)->IsAlive() ) return 0; posTarget = (*itTarget)->GetCurrentPosition( GetArTime() ); } else if( n == 4 && lua_isnumber( L, 3 ) && lua_isnumber( L, 4 ) ) { posTarget.x = lua_tonumber( L, 3 ); posTarget.y = lua_tonumber( L, 4 ); } if( pMonster->GetMonsterBase()->skill_info_list->size() <= static_cast< size_t >( skill_idx ) ) { std::string strError; XStringUtil::Format( strError, "SCRIPT_MonsterSkillCast() : invalid skill index(Monster:%s(%d), SkillIdx:%d)", pMonster->GetName(), pMonster->GetMonsterId(), skill_idx ); LUA()->Log( strError.c_str() ); return 0; } int nSkillId = pMonster->GetMonsterBase()->skill_info_list->at( skill_idx ).skill_id; StructSkill *pSkill = pMonster->GetSkill( nSkillId ); if( !pSkill ) { std::string strError; XStringUtil::Format( strError, "SCRIPT_MonsterSkillCast() : invalid skill id(Monster:%s(%d), SkillIdx:%d, SkillId:%d)", pMonster->GetName(), pMonster->GetMonsterId(), skill_idx, nSkillId ); LUA()->Log( strError.c_str() ); return 0; } // If the skill cannot currently be used, skip it if( ( pSkill->GetSkillBase()->IsPhysicalSkill() && !pMonster->IsSkillCastable() ) || ( pSkill->GetSkillBase()->IsMagicalSkill() && !pMonster->IsMagicCastable() ) ) { pMonster->SetContinueAttack( true ); return 0; } // Ensure the monster continues processAttack even if casting fails if (pMonster->CastSkill(nSkillId, pMonster->GetMonsterBase()->skill_info_list->at(skill_idx).skill_lv, hTarget, posTarget, pMonster->GetLayer()) != RESULT_SUCCESS) { pMonster->SetContinueAttack( true ); } return 0; } int SCRIPT_GetMonsterSkillTargetPosition( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 1 || !lua_isnumber(L, 1) ) { lua_pushnumber( L, 0 ); LUA()->Log( "SCRIPT_GetMonsterSkillTargetPosition() : invalid argument" ); return 1; } AR_HANDLE hMonster = lua_tonumber( L, 1 ); StructMonster::iterator it = StructMonster::get( hMonster ); if( !(*it) || !(*it)->IsMonster() ) { lua_pushnumber( L, 0 ); return 1; } StructMonster * pMonster = static_cast< StructMonster * >( *it ); lua_pushnumber( L, pMonster->GetSkillTargetPosition().x ); lua_pushnumber( L, pMonster->GetSkillTargetPosition().y ); return 2; } int SCRIPT_SetMonsterSkillTargetPosition( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushnumber( L, 0 ); LUA()->Log( "SCRIPT_SetMonsterSkillTargetPosition() : invalid argument" ); return 1; } AR_HANDLE hMonster = lua_tonumber( L, 1 ); StructMonster::iterator it = StructMonster::get( hMonster ); if( !(*it) || !(*it)->IsMonster() ) { lua_pushnumber( L, 0 ); return 1; } StructMonster * pMonster = static_cast< StructMonster * >( *it ); pMonster->SetSkillTargetPosition( ArPosition( lua_tonumber( L, 2 ), lua_tonumber( L, 3 ) ) ); return 0; } int SCRIPT_SetNextAttackableTime( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 1 || !lua_isnumber(L, 1) ) { LUA()->Log( "SCRIPT_SetMonsterNextAttackableTime() : invalid argument" ); return 0; } AR_TIME nDelay = static_cast< int >( lua_tonumber( L, 1 ) * 100 ); AR_HANDLE hTarget = lua_tonumber( L, 2 ); StructCreature::iterator itTarget = StructCreature::get( hTarget ); StructCreature *pTarget = (*itTarget); if( !pTarget || !pTarget->IsInWorld() || !pTarget->IsAlive() || !pTarget->IsAttacking() ) return 0; AR_TIME t = GetArTime(); pTarget->SetNextAttackableTime( t + nDelay ); pTarget->SetMovableTime( t + nDelay ); return 0; } int SCRIPT_GetMonsterID( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 1 || !lua_isnumber(L, 1) ) { lua_pushnumber( L, 0 ); LUA()->Log( "SCRIPT_GetMonsterID() : invalid argument" ); return 1; } AR_HANDLE hMonster = lua_tonumber( L, 1 ); StructMonster::iterator it = StructMonster::get( hMonster ); if( !(*it) || !(*it)->IsMonster() ) { lua_pushnumber( L, 0 ); return 1; } StructMonster * pMonster = static_cast< StructMonster * >( *it ); lua_pushnumber( L, pMonster->GetMonsterId() ); return 1; } int SCRIPT_GetMonsterInfo(struct lua_State* L) { StructPlayer::iterator pit = getPlayer(L, 1); StructPlayer* pPlayer = *pit; StructCreature::iterator pit2 = StructCreature::get(pPlayer->GetTarget()); StructCreature* pCreature = (*pit2); StructMonster* pMonster = (pCreature && pCreature->IsMonster()) ? static_cast(pCreature) : NULL; if (!pMonster || !pMonster->IsMonster()) { lua_pushnumber(L, 0); return 1; } lua_newtable(L); lua_pushnumber(L, 1); lua_pushnumber(L, pMonster->GetMonsterId() ); lua_rawset(L, -3); lua_pushnumber(L, 2); lua_pushnumber(L, pMonster->GetTameCode()); lua_rawset(L, -3); lua_pushnumber(L, 3); lua_pushnumber(L, pMonster->GetNameID()); lua_rawset(L, -3); lua_pushnumber(L, 4); lua_pushnumber(L, pMonster->GetTameItemCode()); lua_rawset(L, -3); lua_pushnumber(L, 5); lua_pushnumber(L, pMonster->GetTamePercetage()); lua_rawset(L, -3); lua_pushnumber(L, 6); lua_pushnumber(L, pMonster->GetHandle()); lua_rawset(L, -3); // 이거 안넣어주면 다운되나? lua_pushstring(L, "n"); lua_pushnumber(L, 6); lua_rawset(L, -3); return 1; } int SCRIPT_GetMonsterInfo_byHandle(struct lua_State* L) { int n = lua_gettop(L); // number of arguments if (n < 1 || !lua_isnumber(L, 1)) { lua_pushnumber(L, 0); LUA()->Log("SCRIPT_GetMonsterID() : invalid argument"); return 1; } AR_HANDLE hMonster = lua_tonumber(L, 1); StructMonster::iterator it = StructMonster::get(hMonster); if (!(*it) || !(*it)->IsMonster()) { lua_pushnumber(L, 0); return 1; } StructMonster* pMonster = static_cast(*it); lua_newtable(L); lua_pushnumber(L, 1); lua_pushnumber(L, pMonster->GetMonsterId()); lua_rawset(L, -3); lua_pushnumber(L, 2); lua_pushnumber(L, pMonster->GetTameCode()); lua_rawset(L, -3); lua_pushnumber(L, 3); lua_pushnumber(L, pMonster->GetNameID()); lua_rawset(L, -3); lua_pushnumber(L, 4); lua_pushnumber(L, pMonster->GetTameItemCode()); lua_rawset(L, -3); lua_pushnumber(L, 5); lua_pushnumber(L, pMonster->GetTamePercetage()); lua_rawset(L, -3); // 이거 안넣어주면 다운되나? lua_pushstring(L, "n"); lua_pushnumber(L, 5); lua_rawset(L, -3); return 1; } int SCRIPT_RespawnNearMonster( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 1 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { LUA()->Log( "SCRIPT_RespawnNearMonster() : invalid argument" ); return 0; } AR_HANDLE hMonster = lua_tonumber( L, 1 ); int monster_id = lua_tonumber( L, 2 ); int cnt = lua_tonumber( L, 3 ); StructMonster::iterator it = StructMonster::get( hMonster ); if( !(*it) || !(*it)->IsMonster() || !(*it)->IsAlive() ) return 0; StructMonster * pMonster = static_cast< StructMonster * >( *it ); AR_UNIT x, y; ArPosition pos = pMonster->GetCurrentPosition( GetArTime() ); for( int i = 0; i < cnt; ++i ) { x = XRandom( pos.x - 60, pos.x + 60 ); y = XRandom( pos.y - 60, pos.y + 60 ); if( GameContent::CollisionToLine( pos.x, pos.y, x, y ) ) { x = pos.x; y = pos.y; if( GameContent::IsBlocked( x, y ) ) { continue; } } StructMonster * pMob = respawnMonster( pos.x, pos.y, pMonster->GetLayer(), monster_id, true, 0, NULL, false ); if( pMob ) { ArPosition targetPos( x, y ); pMob->SetMove( targetPos, pMob->GetRealMoveSpeed() * 3 ); pMob->SetRespawnByScript(); ArcadiaServer::Instance().SetObjectPriority( pMob, ArObject::UPDATE_PRIORITY_HIGHEST ); if( pMonster->IsDungeonRaidMonster() ) { int dungeon_id = DungeonManager::Instance().GetDungeonID( x, y ); if( dungeon_id ) { pMob->SetDungeonRaidMonster(); pMob->CalculateStat(); DungeonManager::Instance().AddToDungeonRaidMonster( pMob, dungeon_id, pMonster->GetLayer() ); } } if( pMonster->IsAttacking() ) { // 몬스터가 스킬 시전할 때만 사용되고 지역 락 걸린 채로 들어오므로 락 없음 // 리젠 위치가 스킬 시전 몬스터와 약간 차이가 있으므로 락이 일부 샐 수 있음 // 락으로 인해 문제 생길 경우 생성 몹을 스킬 시전 몹과 같은 위치에 리젠시키고 // 그 후에 Hate 추가하고, 그 다음에 목표 지점으로 이동하게 할 것 pMob->AddHate( pMonster->GetEnemyHandle(), 1 ); } } } return 0; } #ifndef _USE_NEW_ROAMING_ONLY int SCRIPT_SetWayPointType( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetWayPointType() : invalid argument" ); return 1; } GameContent::SetWayPointType( lua_tonumber( L, 1 ), lua_tonumber( L, 2 ) ); return 0; } int SCRIPT_SetWayPointSpeed( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetWayPointSpeed() : invalid argument" ); return 1; } GameContent::SetWayPointSpeed( lua_tonumber( L, 1 ), lua_tonumber( L, 2 ) ); return 0; } int SCRIPT_AddWayPoint( struct lua_State *L ) { int n = lua_gettop( L ); // number of arguments if( n < 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddWayPoint() : invalid argument" ); return 1; } GameContent::AddWayPoint( lua_tonumber( L, 1 ), lua_tonumber( L, 2 ), lua_tonumber( L, 3 ) ); return 0; } #endif int SCRIPT_FindNPC( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 1 || !lua_isnumber( L, 1 ) ) { lua_pushnumber( L, 0 ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer *pPlayer = *pit; int nNPCID = lua_tonumber( L, 1 ); if( !nNPCID ) { lua_pushnumber( L, 0 ); return 1; } int nLayer = ( nArgCnt >= 2 && lua_isnumber( L, 2 ) ) ? lua_tonumber( L, 2 ) : ( ( pPlayer ) ? pPlayer->GetLayer() : 0 ); StructNPC *pNPC = NULL; extern std::vector< StructNPC* > g_vNPC; for( std::vector< StructNPC * >::iterator it = g_vNPC.begin() ; it != g_vNPC.end() ; ++it ) { if( (*it)->GetNPCID() == nNPCID && (*it)->GetLayer() == nLayer ) { pNPC = (*it); break; } } if( !pNPC ) { lua_pushnumber( L, 0 ); return 1; } lua_pushnumber( L, pNPC->GetHandle() ); return 1; } int SCRIPT_ForceMonsterProcDead( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) { lua_pushnumber( L, -1 ); return 1; } AR_HANDLE hTarget = pPlayer->GetTarget(); if( !hTarget ) { lua_pushnumber( L, -2 ); return 1; } StructCreature::iterator itTarget = StructCreature::get( hTarget ); StructCreature * pTarget = (*itTarget); if( !pTarget || !pTarget->IsMonster() ) { lua_pushnumber( L, -3 ); return 1; } if( !pTarget->IsDead() ) { lua_pushnumber( L, -4 ); return 1; } // 타겟 주변에 락이 걸려있지 않을 경우에만 락을 걸도록 함 // * pTarget의 위치와 완전히 일치하는 곳에 락이 걸려 있지 않거나, 락을 걸어둔 쓰레드가 현재 쓰레드가 아닐 경우 아래와 같은 문제가 발생할 수 있음 // 1. 현재 쓰레드가 pTarget의 락 영역과 일부만 겹치게 락을 걸어둔 경우 // 더블 락 체크에서 assert가 걸리고, 추가로 락을 걸게 되므로 이것이 좌표를 기준으로 서로 교차+동시발생하면 데드락이 됨. // 2. 현재 쓰레드가 완전히 떨어진 영역에 락을 걸어뒀고 pTarget이 있는 곳에는 다른 쓰레드가 일부 영역만 겹치게 락을 걸어둔 경우 // pTarget이 있는 곳에 락을 걸어둔 쓰레드에서 현재 쓰레드가 걸고 있는 완전히 떨어진 영역에 락을 걸려고 시도하면 데드락이 됨 // 3. 다른 쓰레드가 pTarget이 있는 곳에 정확히 일치하게 락을 걸어둔 경우(현재 쓰레드가 완전히 떨어진 영역에 락을 걸어놨든 아니든 상관없음) // 락이 걸려있다고 판단하고 AddHP, damage를 실행하므로 중간에 어디서 락이 풀려버릴지 알 수 없으므로 Access violation이 발생할 수 있음 ArcadiaLock _lock( 0 ); if( !ArcadiaServer::Instance().IsLocked( pTarget ) ) { _lock = ArcadiaServer::Instance().LockObjectWithVisibleRange( pTarget ); } { ARCADIA_LOCK( _lock ); pTarget->AddHP( 1 ); pTarget->damage( pPlayer, 1, false ); } lua_pushnumber( L, 1 ); return 1; } int SCRIPT_ShowSoulStoneCraftWindow( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; SendShowSoulStoneCraftWindow( pPlayer ); lua_pushnumber( L, 1 ); return 1; } int SCRIPT_ShowSoulStoneRepairWindow( struct lua_State *L ) { StructPlayer::iterator pit = getPlayer( L, 1 ); StructPlayer *pPlayer = *pit; if( !pPlayer ) return 0; SendShowSoulStoneRepairWindow( pPlayer ); lua_pushnumber( L, 1 ); return 1; } int SCRIPT_AddMonster2( struct lua_State *L ) { int n = lua_gettop(L); if( n < 1 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddMonster2() : need at least 1 parm" ); _cprint( "%s\n","SCRIPT_AddMonster2() : need at least 1 parm" ); return 1; } int code = (int)lua_tonumber( L, 1 ); int x = (int)lua_tonumber( L, 2 ); int y = (int)lua_tonumber( L, 3 ); unsigned char layer = lua_tonumber( L, 4 ); AR_TIME life_time = lua_tonumber( L, 5 ); int count = 1; StructPlayer::iterator pit = getPlayer( L, n+1 ); StructPlayer *pPlayer = *pit; if( pPlayer && n < 2 ) { x = (int)pPlayer->GetX(); y = (int)pPlayer->GetY(); } else if( !pPlayer && n < 2 ) x, y = 0; //it doesn't really matter because AddMonster2() is conditionally works with SetHome() if( pPlayer && n < 4 ) { layer = pPlayer->GetLayer(); } else if( !pPlayer && n < 4 ) { layer = 0; } if( !life_time || n < 5 ) { life_time = INFINITE_TIME; } std::vector< GameObject * > vObject; int nInstanceDungeonID = InstanceDungeonManager::Instance().GetInstanceDungeonID( ArPosition( x, y ) ); if( nInstanceDungeonID ) { if( !pPlayer && n < 4 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_AddMonster2() : script calling without specified layer" ); return 1; } InstanceDungeonManager::Instance().PendRespawnMonster( nInstanceDungeonID, x, y, layer, code, count, life_time, NULL ); lua_pushnumber( L, 0 ); } else { StructMonster *pMob = StructMonster::AllocMonster( code ); if( !pMob ) { return 0; } pMob->SetGenerateCode( StructMonster::BY_SCRIPT ); pMob->SetCurrentXY( x, y ); pMob->SetCurrentLayer( layer ); pMob->SetLifeTime( life_time ); vObject.push_back( pMob ); ObjectRespawner * pRespawner = new ObjectRespawner( vObject ); ArcadiaServer::Instance().SetObjectPriority( pRespawner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); lua_pushnumber( L, pMob->GetHandle() ); } return 1; } int SCRIPT_RemoveFieldProp( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 1 || !lua_isnumber( L, 1 ) ) { LUA()->Log( "SCRIPT_RemoveFieldProp() : invalid argument" ); lua_pushnumber( L, 0 ); return 1; } AR_HANDLE prop_handle = lua_tonumber( L, 1 ); if( !prop_handle ) { LUA()->Log( "SCRIPT_RemoveFieldProp() : invalid argument" ); lua_pushnumber( L, 0 ); return 1; } StructFieldProp *pProp = NULL; GameObject::iterator it = GameObject::get( prop_handle ); if( (*it)->IsFieldProp() && (*it)->IsInWorld() && !(*it)->bIsDeleteRequested ) { pProp = static_cast< StructFieldProp * >( *it ); pProp->SetDeleteHandler( NULL ); ArcadiaServer::Instance().RemoveObject( pProp ); StructFieldProp::PendFreeFieldProp( pProp ); } lua_pushnumber( L, 1 ); return 1; } int SCRIPT_ShowNPC( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) ) { LUA()->Log( "SCRIPT_ShowNPC() : invalid argument" ); lua_pushnumber( L, 0 ); return 1; } int nX = lua_tonumber( L, 1 ); int nY = lua_tonumber( L, 2 ); int nNPCID = lua_tonumber( L, 3 ); int nCase = lua_tonumber( L, 4 ); if( nCase == 1 ) { char szScript[1024] = { 0, }; s_sprintf( szScript, _countof( szScript ), "add_npc_to_world(%d,%d,0,%d)", nX, nY, nNPCID ); LUA()->RunString( szScript ); return 1; } else if( nCase == 0 ) { StructNPC *pNPC = NULL; extern std::vector< StructNPC* > g_vNPC; for( std::vector< StructNPC * >::iterator it = g_vNPC.begin() ; it != g_vNPC.end() ; ++it ) { if( (*it)->GetNPCID() == nNPCID && (*it)->GetX() == nX && (*it)->GetY() == nY && (*it)->IsInWorld() ) { pNPC = (*it); break; } } if( pNPC ) { ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pNPC ) ); pNPC->onDead( NULL, false ); pNPC->SetDeadTime( 0 ); RemoveNPCFromWorld( pNPC ); ArcadiaServer::Instance().DeleteObject( pNPC ); } return 1; } lua_pushnumber( L, 1 ); return 1; } int SCRIPT_SetHome( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetHome() : invalid argument" ); return 1; } AR_HANDLE MonsterHandle = lua_tonumber( L, 1 ); int WarpX = lua_tonumber( L, 2 ); int WarpY = lua_tonumber( L, 3 ); StructCreature::iterator MonsterRaw = StructCreature::get( MonsterHandle ); StructMonster* pMonster = static_cast< StructMonster * >( *MonsterRaw ); if ( pMonster && pMonster->IsMonster() && pMonster->IsInWorld() && !pMonster->IsDead() ) { pMonster->SetReturnPosition( WarpX, WarpY ); pMonster->comeBackHome( false ); ArcadiaServer::Instance().SetObjectPriority( pMonster, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); return 1; } return 0; }