/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_UNITANIMATION #define INCLUDED_UNITANIMATION #include "ps/CStr.h" #include "simulation2/system/Entity.h" #include class CUnit; class CModel; class CSkeletonAnim; class CObjectEntry; /** * Deals with synchronisation issues between raw animation data (CModel, CSkeletonAnim) * and the simulation system (via CUnit), providing a simple fire-and-forget API to play animations. * (This is really just a component of CUnit and could probably be merged back into that class.) */ class CUnitAnimation { NONCOPYABLE(CUnitAnimation); public: /** * Construct for a given unit, defaulting to the "idle" animation. */ CUnitAnimation(entity_id_t ent, CModel* model, CObjectEntry* object); /** * Change the entity ID associated with this animation * (currently used for playing locational sound effects). */ void SetEntityID(entity_id_t ent); /** * Start playing an animation. * The unit's actor defines the available animations, and if more than one is available * then one is picked at random (with a new random choice each loop). * By default, animations start immediately and run at the given speed with no syncing. * Use SetAnimationSync after this to force a specific timing, if it needs to match the * simulation timing. * Alternatively, set @p desync to a non-zero value (e.g. 0.05) to slightly randomise the * offset and speed, so units don't all move in lockstep. * @param name animation's name ("idle", "walk", etc) * @param once if true then the animation freezes on its last frame; otherwise it loops * @param speed fraction of actor-defined speed to play back at (should typically be 1.0) * @param desync maximum fraction of length/speed to randomly adjust timings (or 0.0 for no desyncing) * @param actionSound sound group name to be played at the 'action' point in the animation, or empty string */ void SetAnimationState(const CStr& name, bool once, float speed, float desync, const CStrW& actionSound); /** * Adjust the speed of the current animation, so that Update(repeatTime) will do a * complete animation loop. * @param repeatTime time for complete loop of animation, in msec */ void SetAnimationSyncRepeat(float repeatTime); /** * Adjust the offset of the current animation, so that Update(actionTime) will advance it * to the 'action' point defined in the actor. * This must be called after SetAnimationSyncRepeat sets the speed. * @param actionTime time between now and when the action should occur, in msec */ void SetAnimationSyncOffset(float actionTime); /** * Advance the animation state. * @param time advance time in msec */ void Update(float time); /** * Regenerate internal animation state from the models in the current unit. * This should be called whenever the unit is changed externally, to keep this in sync. */ void ReloadUnit(CModel* model, const CObjectEntry* object); /** * Reload animation so any changes take immediate effect. */ void ReloadAnimation(); private: /** * Picks a new animation ID from our current state */ void UpdateAnimationID(); struct SModelAnimState { CModel* model; CSkeletonAnim* anim; const CObjectEntry* object; float time; bool pastLoadPos; bool pastActionPos; bool pastSoundPos; }; std::vector m_AnimStates; /** * True if all the current AnimStates are static, so Update() doesn't need * to do any work at all */ bool m_AnimStatesAreStatic; void AddModel(CModel* model, const CObjectEntry* object); entity_id_t m_Entity; CModel* m_Model; const CObjectEntry* m_Object; CStr m_State; CStr m_AnimationID = ""; bool m_Looping; float m_OriginalSpeed; float m_Speed; float m_SyncRepeatTime; float m_Desync; CStrW m_ActionSound; }; #endif // INCLUDED_UNITANIMATION