diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index 33bf5482d3..b6f084fed8 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -430,8 +430,8 @@ void CGameView::Update(float DeltaTime) { if(!m_TrackManager.Update(DeltaTime)) ResetCamera(); + return; } - return; } float delta = powf( m_ViewSnapSmoothness, DeltaTime ); diff --git a/source/ps/Hotkey.cpp b/source/ps/Hotkey.cpp index 1bc4c106ba..8209fbb526 100644 --- a/source/ps/Hotkey.cpp +++ b/source/ps/Hotkey.cpp @@ -620,3 +620,9 @@ bool keyRespondsTo( int hotkey, int sdlkey ) return true; return false; } + +// Returns true if one of the key combinations for the given hotkey is pressed +bool isKeyDown( const CStr& keyname ) +{ + return hotkeys[getKeyCode(keyname)]; +} diff --git a/source/ps/Interact.cpp b/source/ps/Interact.cpp index 02cb315dff..0c832d18cd 100644 --- a/source/ps/Interact.cpp +++ b/source/ps/Interact.cpp @@ -1382,6 +1382,7 @@ void CBuildingPlacer::mouseReleased() { // issue a place object command accross the network CPlaceObject *msg = new CPlaceObject(); + msg->m_IsQueued = hotkeys[HOTKEY_ORDER_QUEUE]; msg->m_Entities = g_Selection.m_selected; msg->m_Template = m_templateName; msg->m_X = (u32) (clickPos.X * 1000); diff --git a/source/ps/KeyName.cpp b/source/ps/KeyName.cpp index 37e80886e2..cb4b1a3e64 100644 --- a/source/ps/KeyName.cpp +++ b/source/ps/KeyName.cpp @@ -288,6 +288,7 @@ void initKeyNameMap() SKeycodeMapping* it = keycodeMapping; while( it->keycode != 0 ) { + debug_printf("adding key %s\n", it->keyname); keymap.insert( std::pair( CStr( it->keyname ).LowerCase(), it->keycode ) ); if( it->altkeyname ) keymap.insert( std::pair( CStr( it->altkeyname ).LowerCase(), it->keycode ) ); diff --git a/source/ps/Network/AllNetMessages.h b/source/ps/Network/AllNetMessages.h index a3a92eeb0a..f682b3e71e 100644 --- a/source/ps/Network/AllNetMessages.h +++ b/source/ps/Network/AllNetMessages.h @@ -210,6 +210,7 @@ END_NMT_CLASS() START_NMT_CLASS(CNetCommand, NMT_NONE) NMT_FIELD(CEntityList, m_Entities) + NMT_FIELD_INT(m_IsQueued, u32, 1) END_NMT_CLASS() DERIVE_NMT_CLASS_(NetCommand, Goto) diff --git a/source/ps/Network/NetMessage.cpp b/source/ps/Network/NetMessage.cpp index 1f2e2c76ff..692bfdb90b 100644 --- a/source/ps/Network/NetMessage.cpp +++ b/source/ps/Network/NetMessage.cpp @@ -89,7 +89,7 @@ void CNetMessage::ScriptingInit() def(NMT_FormationGeneric); } -CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSContext *cx, uintN argc, jsval *argv) +CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSContext *cx, uintN argc, jsval *argv, bool isQueued) { debug_assert(argc >= 1); @@ -160,6 +160,7 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte case NMT_ ## _msg: \ { \ C##_msg *msg = new C##_msg(); \ + msg->m_IsQueued = isQueued; \ msg->m_Entities = entities; \ ReadPosition(msg, m_Target); \ return msg; \ @@ -169,6 +170,7 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte case NMT_ ## _msg: \ { \ C##_msg *msg = new C##_msg(); \ + msg->m_IsQueued = isQueued; \ msg->m_Entities = entities; \ ReadEntity(msg, m_Target); \ return msg; \ @@ -178,6 +180,7 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte case NMT_ ## _msg: \ { \ C##_msg *msg = new C##_msg(); \ + msg->m_IsQueued = isQueued; \ msg->m_Entities = entities; \ ReadEntity(msg, m_Target); \ ReadInt(msg, m_Action); \ @@ -188,6 +191,7 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte case NMT_ ## _msg: \ { \ C##_msg *msg = new C##_msg(); \ + msg->m_IsQueued = isQueued; \ msg->m_Entities = entities; \ ReadInt(msg, m_Type); \ ReadString(msg, m_Name); \ @@ -198,6 +202,7 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte case NMT_ ## _msg: \ { \ C##_msg *msg = new C##_msg(); \ + msg->m_IsQueued = isQueued; \ msg->m_Entities = entities; \ ReadString(msg, m_Template); \ ReadInt(msg, m_X); \ diff --git a/source/ps/Network/NetMessage.h b/source/ps/Network/NetMessage.h index e735ca227c..1f1efec654 100644 --- a/source/ps/Network/NetMessage.h +++ b/source/ps/Network/NetMessage.h @@ -74,7 +74,7 @@ public: */ static void ScriptingInit(); - static CNetCommand *CommandFromJSArgs(const CEntityList &entities, JSContext* cx, uintN argc, jsval* argv); + static CNetCommand *CommandFromJSArgs(const CEntityList &entities, JSContext* cx, uintN argc, jsval* argv, bool isQueued); //These can create a net message without JS args static CNetMessage *CreatePositionMessage( const CEntityList& entities, const int type, CVector2D pos ); diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 5bb382a84b..24e5d5d15d 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -26,6 +26,7 @@ #include "ps/Network/Client.h" #include "ps/Network/Server.h" #include "ps/i18n.h" +#include "ps/Hotkey.h" #include "ps/scripting/JSCollection.h" #include "ps/scripting/JSInterface_Console.h" #include "ps/scripting/JSInterface_Selection.h" @@ -206,10 +207,10 @@ void CreateFormationMessage( std::vector& msgList, CNetMessage* ms // returns: command in serialized form [string] JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { - // at least one for target object, and then 1 or more for the CommandFromJSArgs - JSU_REQUIRE_MIN_PARAMS(2); + // at least one for target object, one for isQueued, and then 1 or more for the CommandFromJSArgs + JSU_REQUIRE_MIN_PARAMS(3); - debug_assert(JSVAL_IS_OBJECT(argv[0])); + JSU_ASSERT(JSVAL_IS_OBJECT(argv[0]), "Argument 0 must be an entity collection."); *rval = JSVAL_NULL; CEntityList entities, msgEntities; @@ -221,6 +222,8 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv msgEntities = entities; std::map entityStore; + + bool isQueued = ToPrimitive(argv[1]); //Destroy old notifiers if we're explicitly being reassigned for ( size_t i=0; i < entities.size(); i++) @@ -248,7 +251,7 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv else msgEntities.push_back( entities[i] ); } - CNetMessage* msg = CNetMessage::CommandFromJSArgs(msgEntities, cx, argc-1, argv+1); + CNetMessage* msg = CNetMessage::CommandFromJSArgs(msgEntities, cx, argc-2, argv+2, isQueued); if (!msg) { delete msg; @@ -310,6 +313,17 @@ JSBool isFormationLocked( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval *rval = entity->GetFormation()->IsLocked() ? JS_TRUE : JS_FALSE; return JS_TRUE; } + +// Get the state of a given hotkey (from the hotkeys file) +JSBool isOrderQueued( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + JSU_REQUIRE_NO_PARAMS(); + + *rval = ToJSVal(hotkeys[HOTKEY_ORDER_QUEUE]); + return JS_TRUE; +} + + //----------------------------------------------------------------------------- // Techs //----------------------------------------------------------------------------- @@ -1364,6 +1378,7 @@ JSFunctionSpec ScriptFunctionTable[] = JS_FUNC(setCursor, setCursor, 1) JS_FUNC(getCursorName, getCursorName, 0) JS_FUNC(getFPS, getFPS, 0) + JS_FUNC(isOrderQueued, isOrderQueued, 1) // Miscellany JS_FUNC(v3dist, v3dist, 2) diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index 4413ff91c9..b77aa871bb 100644 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -91,7 +91,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, float len = delta.length(); - if( len < 0.1f ) + if( len < 0.01f ) return( ALREADY_AT_DESTINATION ); // Curve smoothing. @@ -271,7 +271,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli case COLLISION_OVERLAPPING_OBJECTS: return( false ); case COLLISION_WITH_DESTINATION: - // We're here... + // We're as close as we can get... m_orderQueue.pop_front(); //entf_clear(ENTF_IS_RUNNING); //entf_clear(ENTF_SHOULD_RUN); diff --git a/source/simulation/PathfindEngine.cpp b/source/simulation/PathfindEngine.cpp index 7fbb052947..beacf16169 100644 --- a/source/simulation/PathfindEngine.cpp +++ b/source/simulation/PathfindEngine.cpp @@ -24,7 +24,7 @@ void CPathfindEngine::requestPath( HEntity entity, const CVector2D& destination entity->m_orderQueue.push_front( waypoint ); } -void CPathfindEngine::requestLowLevelPath( HEntity entity, const CVector2D& destination, bool contact, float radius ) +void CPathfindEngine::requestLowLevelPath( HEntity entity, const CVector2D& destination, bool UNUSED(contact), float radius ) { PROFILE_START("Pathfinding"); @@ -35,45 +35,42 @@ void CPathfindEngine::requestLowLevelPath( HEntity entity, const CVector2D& dest std::vector path = mLowPathfinder.getLastPath(); if( path.size() > 0 ) { - std::vector::iterator it; + // Push the path onto the front of our order queue in reverse order, + // so that we run through it before continuing other orders. + CEntityOrder node; - for( it = path.begin(); (it+1) != path.end(); it++ ) - { - if ( !contact ) - { - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; - } - else - { - // TODO: Is this right? - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; - } - node.m_data[0].location = *it; - entity->m_orderQueue.push_back(node); - } + // Hack to make pathfinding slightly more precise: // If the radius was 0, make the final node be exactly at the destination // (otherwise, go to wherever the pathfinder tells us since we just want to be in range) - CVector2D finalDest = (radius==0 ? destination : (*it)); - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + CVector2D finalDest = (radius==0 ? destination : path[path.size()-1]); + node.m_type = CEntityOrder::ORDER_PATH_END_MARKER; // push end marker (used as a sentinel when repathing) node.m_data[0].location = finalDest; - entity->m_orderQueue.push_back(node); - node.m_type = CEntityOrder::ORDER_PATH_END_MARKER; + entity->m_orderQueue.push_front(node); + node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; // push final goto step node.m_data[0].location = finalDest; - entity->m_orderQueue.push_back(node); + entity->m_orderQueue.push_front(node); + + for( int i = ((int) path.size()) - 2; i >= 0; i-- ) + { + node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; // TODO: For non-contact paths, do we want some other order type? + node.m_data[0].location = path[i]; + entity->m_orderQueue.push_front(node); + } } else { // Hack to make pathfinding slightly more precise: - // If radius = 0, we are still moving within the same tile, so add a GOTO order anyway + // If radius = 0, we have an empty path but the user still wants us to move + // within the same tile, so add a GOTO order anyway if(radius == 0) { CEntityOrder node; - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; - node.m_data[0].location = destination; - entity->m_orderQueue.push_back(node); node.m_type = CEntityOrder::ORDER_PATH_END_MARKER; node.m_data[0].location = destination; - entity->m_orderQueue.push_back(node); + entity->m_orderQueue.push_front(node); + node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + node.m_data[0].location = destination; + entity->m_orderQueue.push_front(node); } } } diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index bf451041b7..14ae1ae82e 100644 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -148,7 +148,7 @@ void CSimulation::Simulate() // Task them all to a point within a radius of the target, radius depends upon // the number of units in the group. -void RandomizeLocations(CEntityOrder order, const vector &entities, bool clearQueue) +void RandomizeLocations(CEntityOrder order, const vector &entities, bool isQueued) { vector::const_iterator it; float radius = 2.0f * sqrt( (float)entities.size() - 1 ); @@ -181,14 +181,14 @@ void RandomizeLocations(CEntityOrder order, const vector &entities, bo if( randomizedOrder.m_data[0].location.y >= mapsize ) randomizedOrder.m_data[0].location.y = mapsize; - if( clearQueue ) + if( !isQueued ) (*it)->clearOrders(); (*it)->pushOrder( randomizedOrder ); } } -void FormationLocations(CEntityOrder order, const vector &entities, bool clearQueue) +void FormationLocations(CEntityOrder order, const vector &entities, bool isQueued) { CVector2D upvec(0.0f, 1.0f); vector::const_iterator it = entities.begin(); @@ -223,20 +223,20 @@ void FormationLocations(CEntityOrder order, const vector &entities, bo if( orderCopy.m_data[0].location.y >= mapsize ) orderCopy.m_data[0].location.y = mapsize; - if( clearQueue ) + if( !isQueued ) (*it)->clearOrders(); (*it)->pushOrder( orderCopy ); } } -void QueueOrder(CEntityOrder order, const vector &entities, bool clearQueue) +void QueueOrder(CEntityOrder order, const vector &entities, bool isQueued) { vector::const_iterator it; for (it = entities.begin(); it < entities.end(); it++) { - if( clearQueue ) + if( !isQueued ) (*it)->clearOrders(); (*it)->pushOrder( order ); @@ -246,46 +246,51 @@ void QueueOrder(CEntityOrder order, const vector &entities, bool clear uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNUSED(userdata)) { CEntityOrder order; - bool clearQueue = true; + bool isQueued = true; #define ENTITY_POSITION(_msg, _order) do\ { \ _msg *msg=(_msg *)pMsg; \ + isQueued = msg->m_IsQueued != 0; \ order.m_type=CEntityOrder::_order; \ order.m_data[0].location.x=(float)msg->m_TargetX; \ order.m_data[0].location.y=(float)msg->m_TargetY; \ - RandomizeLocations(order, msg->m_Entities, clearQueue); \ + RandomizeLocations(order, msg->m_Entities, isQueued); \ } while(0) #define ENTITY_POSITION_FORM(_msg, _order) do\ { \ _msg *msg=(_msg *)pMsg; \ + isQueued = msg->m_IsQueued != 0; \ order.m_type=CEntityOrder::_order; \ order.m_data[0].location.x=(float)msg->m_TargetX; \ order.m_data[0].location.y=(float)msg->m_TargetY; \ - FormationLocations(order, msg->m_Entities, clearQueue); \ + FormationLocations(order, msg->m_Entities, isQueued); \ } while(0) #define ENTITY_ENTITY(_msg, _order) do\ { \ _msg *msg=(_msg *)pMsg; \ + isQueued = msg->m_IsQueued != 0; \ order.m_type=CEntityOrder::_order; \ order.m_data[0].entity=msg->m_Target; \ - QueueOrder(order, msg->m_Entities, clearQueue); \ + QueueOrder(order, msg->m_Entities, isQueued); \ } while(0) #define ENTITY_ENTITY_INT(_msg, _order) do\ { \ _msg *msg=(_msg *)pMsg; \ + isQueued = msg->m_IsQueued != 0; \ order.m_type=CEntityOrder::_order; \ order.m_data[0].entity=msg->m_Target; \ order.m_data[1].data=msg->m_Action; \ - QueueOrder(order, msg->m_Entities, clearQueue); \ + QueueOrder(order, msg->m_Entities, isQueued); \ } while(0) #define ENTITY_INT_STRING(_msg, _order) do\ { \ _msg *msg=(_msg *)pMsg; \ + isQueued = msg->m_IsQueued != 0; \ order.m_type=CEntityOrder::_order; \ order.m_data[0].string=msg->m_Name; \ order.m_data[1].data=msg->m_Type; \ - QueueOrder(order, msg->m_Entities, clearQueue); \ + QueueOrder(order, msg->m_Entities, isQueued); \ } while(0) switch (pMsg->GetType()) @@ -293,6 +298,7 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU case NMT_AddWaypoint: { CAddWaypoint *msg=(CAddWaypoint *)pMsg; + isQueued = msg->m_IsQueued != 0; order.m_type=CEntityOrder::ORDER_LAST; order.m_data[0].location.x=(float)msg->m_TargetX; order.m_data[0].location.y=(float)msg->m_TargetY; @@ -351,6 +357,7 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU case NMT_PlaceObject: { CPlaceObject *msg = (CPlaceObject *) pMsg; + isQueued = msg->m_IsQueued != 0; // Figure out the player CPlayer* player = 0;