diff --git a/code/model/animation/modelanimation.cpp b/code/model/animation/modelanimation.cpp index 722e8d2b4bd..a4a321614b5 100644 --- a/code/model/animation/modelanimation.cpp +++ b/code/model/animation/modelanimation.cpp @@ -1062,12 +1062,31 @@ namespace animation { return true; } + bool ModelAnimationSet::advanceMoveableToFinal(polymodel_instance* pmi, const SCP_string& name) const { + SCP_string lowername = name; + SCP_tolower(lowername); + auto moveable = m_moveableSet.find(lowername); + if (moveable == m_moveableSet.end()) + return false; + + moveable->second->advanceToFinalState(pmi); + return true; + } + void ModelAnimationSet::initializeMoveables(polymodel_instance* pmi) { for(const auto& moveable : m_moveableSet){ moveable.second->initialize(this, pmi); } } + void ModelAnimationMoveable::advanceToFinalState(polymodel_instance* pmi) { + auto it = m_instances.find(pmi->id); + if (it == m_instances.end() || !it->second.animation) + return; + + it->second.animation->start(pmi, ModelAnimationDirection::FWD, true, true); + } + SCP_vector ModelAnimationSet::getRegisteredMoveables() const { SCP_vector ret; diff --git a/code/model/animation/modelanimation.h b/code/model/animation/modelanimation.h index 34372cac69b..13d89007220 100644 --- a/code/model/animation/modelanimation.h +++ b/code/model/animation/modelanimation.h @@ -286,6 +286,8 @@ namespace animation { virtual void update(polymodel_instance* pmi, const SCP_vector& args) = 0; virtual void initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) = 0; + + void advanceToFinalState(polymodel_instance* pmi); }; @@ -368,6 +370,7 @@ namespace animation { SCP_set getRegisteredAnimNames() const; bool updateMoveable(polymodel_instance* pmi, const SCP_string& name, const SCP_vector& args) const; + bool advanceMoveableToFinal(polymodel_instance* pmi, const SCP_string& name) const; void initializeMoveables(polymodel_instance* pmi); SCP_vector getRegisteredMoveables() const; diff --git a/code/model/animation/modelanimation_segments.cpp b/code/model/animation/modelanimation_segments.cpp index b208c79bed1..802e927cba8 100644 --- a/code/model/animation/modelanimation_segments.cpp +++ b/code/model/animation/modelanimation_segments.cpp @@ -1522,7 +1522,7 @@ namespace animation { ik->addNode(submodel, chainlink.constraint.get()); } - ik->solve(m_targetPosition, &(*m_targetRotation)); + ik->solve(m_targetPosition, m_targetRotation ? &*m_targetRotation : nullptr); auto chainlink_it = m_chain.cbegin(); for(const auto& solvedlink : *ik){ diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 06bf74a0155..941595d4644 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -627,6 +627,7 @@ SCP_vector Operators = { { "trigger-ship-animation", OP_TRIGGER_ANIMATION_NEW, 3, 7, SEXP_ACTION_OPERATOR, }, //Lafiel { "stop-looping-animation", OP_STOP_LOOPING_ANIMATION, 3, 3, SEXP_ACTION_OPERATOR, }, //Lafiel { "update-moveable-animation", OP_UPDATE_MOVEABLE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, //Lafiel + { "advance-moveable-animation", OP_ADVANCE_MOVEABLE, 2, 2, SEXP_ACTION_OPERATOR, }, //Lafiel //Coordinate Manipulation Sub-Category { "set-object-position", OP_SET_OBJECT_POSITION, 4, 4, SEXP_ACTION_OPERATOR, }, // WMC @@ -2870,7 +2871,8 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad break; } - case OP_UPDATE_MOVEABLE: { + case OP_UPDATE_MOVEABLE: + case OP_ADVANCE_MOVEABLE:{ //Second OP name SCP_string name = CTEXT(CDR(ship_node)); SCP_tolower(name); @@ -23557,6 +23559,19 @@ void sexp_update_moveable_animation(int node) Ship_info[ship_entry->shipp()->ship_info_index].animations.updateMoveable(model_get_instance(ship_entry->shipp()->model_instance_num), name, args); } +void sexp_advance_moveable_animation(int node) +{ + auto ship_entry = eval_ship(node); + if (!ship_entry || !ship_entry->has_shipp()) + return; + + node = CDR(node); + + SCP_string name(CTEXT(node)); + + Ship_info[ship_entry->shipp()->ship_info_index].animations.advanceMoveableToFinal(model_get_instance(ship_entry->shipp()->model_instance_num), name); +} + void sexp_add_remove_escort(int node) { int flag; @@ -30675,6 +30690,11 @@ int eval_sexp(int cur_node, int referenced_node) sexp_update_moveable_animation(node); break; + case OP_ADVANCE_MOVEABLE: + sexp_val = SEXP_TRUE; + sexp_advance_moveable_animation(node); + break; + case OP_STOP_LOOPING_ANIMATION: sexp_val = SEXP_TRUE; sexp_stop_looping_animation(node); @@ -32013,6 +32033,7 @@ int query_operator_return_type(int op) case OP_SET_ALPHA_MULT: case OP_TRIGGER_ANIMATION_NEW: case OP_UPDATE_MOVEABLE: + case OP_ADVANCE_MOVEABLE: case OP_STOP_LOOPING_ANIMATION: case OP_CONTAINER_ADD_TO_LIST: case OP_CONTAINER_REMOVE_FROM_LIST: @@ -34939,6 +34960,12 @@ int query_operator_argument_type(int op_index, int argnum) else return OPF_NUMBER; + case OP_ADVANCE_MOVEABLE: + if (argnum == 0) + return OPF_SHIP; + else + return OPF_ANIMATION_NAME; + case OP_IS_CONTAINER_EMPTY: case OP_GET_CONTAINER_SIZE: if (argnum == 0) { @@ -37210,6 +37237,7 @@ int get_category(int op_id) case OP_DESTROY_INSTANTLY_WITH_DEBRIS: case OP_TRIGGER_ANIMATION_NEW: case OP_UPDATE_MOVEABLE: + case OP_ADVANCE_MOVEABLE: case OP_NAV_SET_COLOR: case OP_NAV_SET_VISITED_COLOR: case OP_CONTAINER_ADD_TO_LIST: @@ -37547,6 +37575,7 @@ int get_subcategory(int op_id) case OP_SET_ALPHA_MULT: case OP_TRIGGER_ANIMATION_NEW: case OP_UPDATE_MOVEABLE: + case OP_ADVANCE_MOVEABLE: case OP_STOP_LOOPING_ANIMATION: return CHANGE_SUBCATEGORY_MODELS_AND_TEXTURES; @@ -42871,6 +42900,13 @@ SCP_vector Sexp_help = { "\tThree optional numbers: x, y, z rotation target relative to base, in degrees\r\n" }, + { OP_ADVANCE_MOVEABLE, "advance-moveable-animation\r\n" + "\tAdvances a moveable animation to its final state instantly.\r\n" + "Takes 2 arguments...\r\n" + "\t1: The ship (ship must be in-mission).\r\n" + "\t2: The name of the moveable.\r\n" + }, + { OP_TOGGLE_ASTEROID_FIELD, "toggle-asteroid-field\r\n" "\tTurns an existing asteroid or debris field on/off. Will do nothing if no field is defined in the mission file or set with sexps.\r\n" "\tNote that this differs from actually removing and adding a field in that it only toggles a field from being rendered or calculated.\r\n" diff --git a/code/parse/sexp.h b/code/parse/sexp.h index 6e5c8b8b4e9..ec111c0295f 100644 --- a/code/parse/sexp.h +++ b/code/parse/sexp.h @@ -899,6 +899,7 @@ enum : int { OP_DESTROY_INSTANTLY_WITH_DEBRIS, // Asteroth OP_TRIGGER_ANIMATION_NEW, // Lafiel OP_UPDATE_MOVEABLE, // Lafiel + OP_ADVANCE_MOVEABLE, // Lafiel OP_NAV_SET_COLOR, // Goober5000 OP_NAV_SET_VISITED_COLOR, // Goober5000 OP_CONTAINER_ADD_TO_LIST, // Karajorma/jg18 diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 77d9e3234a9..1915f0cfb79 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -2311,6 +2311,23 @@ ADE_FUNC(updateSubmodelMoveable, l_Ship, "string name, table values", return Ship_info[shipp->ship_info_index].animations.updateMoveable(model_get_instance(shipp->model_instance_num), name, valuesMoveable) ? ADE_RETURN_TRUE : ADE_RETURN_FALSE; } +ADE_FUNC(advanceSubmodelMoveableToFinal, l_Ship, "string name", + "Advances a moveable animation to its final state immediately. Name is the name of the moveable.", + "boolean", "True if successful, false or nil otherwise") +{ + object_h* objh; + const char* name = nullptr; + + if (!ade_get_args(L, "os", l_Ship.GetPtr(&objh), &name)) + return ADE_RETURN_NIL; + + if (!objh->isValid()) + return ADE_RETURN_NIL; + + ship* shipp = &Ships[objh->objp()->instance]; + return Ship_info[shipp->ship_info_index].animations.advanceMoveableToFinal(model_get_instance(shipp->model_instance_num), name) ? ADE_RETURN_TRUE : ADE_RETURN_FALSE; +} + ADE_FUNC(warpIn, l_Ship, NULL, "Warps ship in", "boolean", "True if successful, or nil if ship handle is invalid") { object_h *objh;