diff --git a/binaries/data/mods/official/gui/test/functions_sim_entity.js b/binaries/data/mods/official/gui/test/functions_sim_entity.js index eda985c912..92f3acd948 100644 --- a/binaries/data/mods/official/gui/test/functions_sim_entity.js +++ b/binaries/data/mods/official/gui/test/functions_sim_entity.js @@ -47,7 +47,7 @@ function worldClickHandler(event) if ( getCursorName() == "cursor-rally" ) { for ( var i=0; iAddTransition( NCS_INGAME, ( uint )NMT_PRODUCE, NCS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NCS_INGAME, ( uint )NMT_PLACE_OBJECT, NCS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NCS_INGAME, ( uint )NMT_RUN, NCS_INGAME, (void*)&OnInGame, pContext ); + pSession->AddTransition( NCS_INGAME, ( uint )NMT_SET_RALLY_POINT, NCS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NCS_INGAME, ( uint )NMT_NOTIFY_REQUEST, NCS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NCS_INGAME, ( uint )NMT_FORMATION_GOTO, NCS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NCS_INGAME, ( uint )NMT_FORMATION_GENERIC, NCS_INGAME, (void*)&OnInGame, pContext ); diff --git a/source/network/NetMessage.cpp b/source/network/NetMessage.cpp index e6f65268c1..96f7e53802 100644 --- a/source/network/NetMessage.cpp +++ b/source/network/NetMessage.cpp @@ -213,6 +213,7 @@ void CNetMessage::ScriptingInit() g_ScriptingHost.DefineConstant( "NMT_GENERIC", NMT_GENERIC ); g_ScriptingHost.DefineConstant( "NMT_PRODUCE", NMT_PRODUCE ); g_ScriptingHost.DefineConstant( "NMT_PLACE_OBJECT", NMT_PLACE_OBJECT ); + g_ScriptingHost.DefineConstant( "NMT_SET_RALLY_POINT", NMT_SET_RALLY_POINT ); g_ScriptingHost.DefineConstant( "NMT_NOTIFY_REQUEST", NMT_NOTIFY_REQUEST ); g_ScriptingHost.DefineConstant( "NMT_FORMATION_GOTO", NMT_FORMATION_GOTO ); g_ScriptingHost.DefineConstant( "NMT_FORMATION_GENERIC", NMT_FORMATION_GENERIC ); @@ -387,6 +388,41 @@ CNetMessage* CNetMessage::CommandFromJSArgs( return pMessage; } + case NMT_SET_RALLY_POINT: + { + CSetRallyPointMessage* pMessage = new CSetRallyPointMessage; + if ( !pMessage ) return NULL; + + pMessage->m_IsQueued = isQueued; + pMessage->m_Entities = entities; + + try + { + if ( idx + 2 > argc ) + { + JS_ReportError( pContext, "Too few parameters!" ); + return NULL; + } + + if ( !JSVAL_IS_INT( argv[ idx ] ) || + !JSVAL_IS_INT( argv[ idx + 1 ] ) ) + { + JS_ReportError( pContext, "Parameter type error!" ); + return NULL; + } + + pMessage->m_TargetX = ToPrimitive< int >( argv[ idx++ ] ); + pMessage->m_TargetY = ToPrimitive< int >( argv[ idx++ ] ); + } + catch ( PSERROR_Scripting_ConversionFailed ) + { + JS_ReportError( pContext, "Invalid location" ); + return NULL; + } + + return pMessage; + } + case NMT_FORMATION_GOTO: { CFormationGotoMessage* pMessage = new CFormationGotoMessage; @@ -722,6 +758,18 @@ CNetMessage* CNetMessage::CreatePositionMessage( return pMessage; } + case NMT_SET_RALLY_POINT: + { + CSetRallyPointMessage *pMessage = new CSetRallyPointMessage; + if ( !pMessage ) return NULL; + + pMessage->m_Entities = entities; + pMessage->m_TargetX = pos.x; + pMessage->m_TargetY = pos.y; + + return pMessage; + } + case NMT_FORMATION_GOTO: { CFormationGotoMessage *pMessage = new CFormationGotoMessage; @@ -758,6 +806,7 @@ CNetMessage* CNetMessage::CreateEntityIntMessage( pMessage->m_Entities = entities; pMessage->m_Target = target; pMessage->m_Action = action; + pMessage->m_Run = false; return pMessage; } @@ -911,6 +960,10 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData, pNewMessage = new CRunMessage; break; + case NMT_SET_RALLY_POINT: + pNewMessage = new CSetRallyPointMessage; + break; + case NMT_NOTIFY_REQUEST: pNewMessage = new CNotifyRequestMessage; break; @@ -1124,6 +1177,46 @@ CNetMessage* CNetMessageFactory::CreateMessage( } break; + case NMT_SET_RALLY_POINT: + { + CSetRallyPointMessage *pMessage = new CSetRallyPointMessage; + if ( !pMessage ) return NULL; + + pMessage->m_IsQueued = queued; + pMessage->m_Entities = entities; + + try + { + if ( idx + 2 > argc ) + { + STMT( + JS_ReportError( pContext, "Too few parameters!" ); + return NULL; + ); + } + + if ( !JSVAL_IS_INT( argv[ idx ] ) || + !JSVAL_IS_INT( argv[ idx + 1 ] ) ) + { + STMT( + JS_ReportError( pContext, "Parameter type error!" ); + return NULL; + ); + } + + pMessage->m_TargetX = ToPrimitive< int >( argv[ idx++ ] ); + pMessage->m_TargetY = ToPrimitive< int >( argv[ idx++ ] ); + } + catch ( PSERROR_Scripting_ConversionFailed ) + { + JS_ReportError( pContext, "Invalid location" ); + return NULL; + } + + pNewMessage = pMessage; + } + break; + case NMT_FORMATION_GOTO: { CFormationGotoMessage *pMessage = new CFormationGotoMessage; diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index e89356f9b9..4f7d58d457 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -56,6 +56,7 @@ enum NetMessageType NMT_PRODUCE, NMT_PLACE_OBJECT, NMT_RUN, + NMT_SET_RALLY_POINT, NMT_NOTIFY_REQUEST, NMT_FORMATION_GOTO, NMT_FORMATION_GENERIC, @@ -198,6 +199,11 @@ DERIVE_NMT_CLASS_(Command, AddWaypoint, NMT_ADD_WAYPOINT) NMT_FIELD_INT(m_TargetY, u32, 2) END_NMT_CLASS() +DERIVE_NMT_CLASS_(Command, SetRallyPoint, NMT_SET_RALLY_POINT) + NMT_FIELD_INT(m_TargetX, u32, 2) + NMT_FIELD_INT(m_TargetY, u32, 2) +END_NMT_CLASS() + DERIVE_NMT_CLASS_(Command, Generic, NMT_GENERIC) NMT_FIELD(HEntity, m_Target) NMT_FIELD_INT(m_Action, u32, 4) diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index bfc61dc9c6..cdae8c5b0f 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -144,6 +144,7 @@ bool CNetServer::SetupSession( CNetSession* pSession ) pSession->AddTransition( NSS_INGAME, ( uint )NMT_PRODUCE, NSS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NSS_INGAME, ( uint )NMT_PLACE_OBJECT, NSS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NSS_INGAME, ( uint )NMT_RUN, NSS_INGAME, (void*)&OnInGame, pContext ); + pSession->AddTransition( NSS_INGAME, ( uint )NMT_SET_RALLY_POINT, NSS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NSS_INGAME, ( uint )NMT_NOTIFY_REQUEST, NSS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NSS_INGAME, ( uint )NMT_FORMATION_GOTO, NSS_INGAME, (void*)&OnInGame, pContext ); pSession->AddTransition( NSS_INGAME, ( uint )NMT_FORMATION_GENERIC, NSS_INGAME, (void*)&OnInGame, pContext ); diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index ed572ac34e..2942c18431 100644 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -827,10 +827,19 @@ void CEntity::PushOrder( const CEntityOrder& order ) CEventPrepareOrder evt( order.m_target_entity, order.m_type, order.m_action, order.m_produce_name ); if( DispatchEvent(&evt) ) { - m_orderQueue.push_back( order ); - if(evt.m_notifyType != CEntityListener::NOTIFY_NONE) + if (order.m_type == CEntityOrder::ORDER_SET_RALLY_POINT) { - CheckListeners( evt.m_notifyType, evt.m_notifySource ); + // It doesn't make sense to queue this type of order; just set the rally point + entf_set(ENTF_HAS_RALLY_POINT); + m_rallyPoint = order.m_target_location; + } + else + { + m_orderQueue.push_back( order ); + if(evt.m_notifyType != CEntityListener::NOTIFY_NONE) + { + CheckListeners( evt.m_notifyType, evt.m_notifySource ); + } } } } diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 98b3589edf..eb06a6556c 100644 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -189,7 +189,7 @@ public: CVector2D m_orientation_unclamped; - CVector3D m_rallyPoint; // valid iff ENT_HAS_RALLY_POINT + CVector2D m_rallyPoint; // valid iff ENT_HAS_RALLY_POINT // If the actor's current transform data is valid (i.e. the entity hasn't // moved since it was last calculated, and the terrain hasn't been changed). @@ -414,7 +414,7 @@ public: jsval_t HasRallyPoint( JSContext* cx, uintN argc, jsval* argv ); jsval_t GetRallyPoint( JSContext* cx, uintN argc, jsval* argv ); - jsval_t SetRallyPoint( JSContext* cx, uintN argc, jsval* argv ); + jsval_t SetRallyPointAtCursor( JSContext* cx, uintN argc, jsval* argv ); jsval_t AddAura( JSContext* cx, uintN argc, jsval* argv ); jsval_t RemoveAura( JSContext* cx, uintN argc, jsval* argv ); diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index 39478bbe1d..efbd8c2a40 100644 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -96,8 +96,9 @@ public: ORDER_GENERIC_NOPATHING, // 11 ORDER_PRODUCE, // 12 ORDER_START_CONSTRUCTION, // 13 - ORDER_NOTIFY_REQUEST, // 14 - ORDER_LAST // 15 + ORDER_SET_RALLY_POINT, // 14 + ORDER_NOTIFY_REQUEST, // 15 + ORDER_LAST // 16 }; EOrderType m_type; diff --git a/source/simulation/EntityRendering.cpp b/source/simulation/EntityRendering.cpp index be1b5d02b2..2df051c9f3 100644 --- a/source/simulation/EntityRendering.cpp +++ b/source/simulation/EntityRendering.cpp @@ -565,8 +565,8 @@ void CEntity::RenderRallyPoint() if( !m_visible ) return; - if ( !entf_get(ENTF_HAS_RALLY_POINT) || g_Selection.m_unitUITextures.find(m_base->m_rallyName) == - g_Selection.m_unitUITextures.end() ) + if ( !entf_get(ENTF_HAS_RALLY_POINT) || + g_Selection.m_unitUITextures.find(m_base->m_rallyName) == g_Selection.m_unitUITextures.end() ) { return; } @@ -584,8 +584,10 @@ void CEntity::RenderRallyPoint() CTexture tex; tex.SetHandle( g_Selection.m_unitUITextures[m_base->m_rallyName] ); sprite.SetTexture(&tex); - CVector3D rally = m_rallyPoint; - rally.Y += m_base->m_rallyHeight/2.f + .1f; + // Place the sprite slightly above ground/water level. + float terrainHeight = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(m_rallyPoint); + float height = std::max(terrainHeight, g_Renderer.GetWaterManager()->m_WaterHeight) + 0.5f; + CVector3D rally(m_rallyPoint.x, height, m_rallyPoint.y); sprite.SetTranslation(rally); sprite.Render(); } diff --git a/source/simulation/EntityScriptInterface.cpp b/source/simulation/EntityScriptInterface.cpp index 461a2269d7..dcbcd88f84 100644 --- a/source/simulation/EntityScriptInterface.cpp +++ b/source/simulation/EntityScriptInterface.cpp @@ -15,6 +15,7 @@ #include "renderer/WaterManager.h" #include "scripting/ScriptableComplex.inl" #include "ps/scripting/JSCollection.h" +#include "network/NetMessage.h" #include "Aura.h" #include "Collision.h" @@ -82,7 +83,7 @@ void CEntity::ScriptingInit() AddMethod( "findSector", 4); AddMethod( "getHeight", 0 ); AddMethod( "hasRallyPoint", 0 ); - AddMethod( "setRallyPoint", 0 ); + AddMethod( "setRallyPointAtCursor", 0 ); AddMethod( "getRallyPoint", 0 ); AddMethod( "onDamaged", 1 ); AddMethod( "getVisibleEntities", 0 ); @@ -821,12 +822,19 @@ jsval_t CEntity::HasRallyPoint( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval } jsval_t CEntity::GetRallyPoint( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { - return ToJSVal( m_rallyPoint ); + CVector3D v3d( m_rallyPoint.x, m_rallyPoint.y, 0 ); + return ToJSVal( v3d ); } -jsval_t CEntity::SetRallyPoint( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +jsval_t CEntity::SetRallyPointAtCursor( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { - entf_set(ENTF_HAS_RALLY_POINT); - m_rallyPoint = g_Game->GetView()->GetCamera()->GetWorldCoordinates(true); + // Queue an order over the network to set a rally point + CSetRallyPointMessage* msg = new CSetRallyPointMessage; + msg->m_Entities = CEntityList(me); + msg->m_IsQueued = true; + CVector3D point = g_Game->GetView()->GetCamera()->GetWorldCoordinates(true); + msg->m_TargetX = (int) point.X; + msg->m_TargetY = (int) point.Z; + g_Game->GetSimulation()->QueueLocalCommand(msg); return JS_TRUE; } diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index 923ab4ba18..74cc84f38e 100644 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -371,10 +371,12 @@ size_t CSimulation::TranslateMessage(CNetMessage* pMsg, size_t clientMask, void* case NMT_PATROL: ENTITY_POSITION(CPatrolMessage, ORDER_PATROL); break; + case NMT_SET_RALLY_POINT: + ENTITY_POSITION(CSetRallyPointMessage, ORDER_SET_RALLY_POINT); + break; case NMT_FORMATION_GOTO: ENTITY_POSITION_FORM(CFormationGotoMessage, ORDER_GOTO); break; - //TODO: make formation move to within range of target and then attack normally case NMT_GENERIC: ENTITY_ENTITY_INT_BOOL(CGenericMessage, ORDER_GENERIC); diff --git a/source/simulation/scripting/SimulationScriptInit.cpp b/source/simulation/scripting/SimulationScriptInit.cpp index ca2b2a94c9..945c5a3ccd 100644 --- a/source/simulation/scripting/SimulationScriptInit.cpp +++ b/source/simulation/scripting/SimulationScriptInit.cpp @@ -53,6 +53,7 @@ void SimulationScriptInit() g_ScriptingHost.DefineConstant( "ORDER_PATROL", CEntityOrder::ORDER_PATROL ); g_ScriptingHost.DefineConstant( "ORDER_GENERIC", CEntityOrder::ORDER_GENERIC ); g_ScriptingHost.DefineConstant( "ORDER_PRODUCE", CEntityOrder::ORDER_PRODUCE ); + g_ScriptingHost.DefineConstant( "ORDER_SET_RALLY_POINT", CEntityOrder::ORDER_SET_RALLY_POINT ); g_ScriptingHost.DefineConstant( "ORDER_START_CONSTRUCTION", CEntityOrder::ORDER_START_CONSTRUCTION ); }