123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- /****************************************************************************
- Copyright (c) 2008-2010 Ricardo Quesada
- Copyright (c) 2009 Valentin Milea
- Copyright (c) 2010-2012 cocos2d-x.org
- Copyright (c) 2011 Zynga Inc.
- Copyright (c) 2013-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
-
- http://www.cocos2d-x.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- ****************************************************************************/
- #include "2d/CCActionManager.h"
- #include "2d/CCNode.h"
- #include "2d/CCAction.h"
- #include "base/CCScheduler.h"
- #include "base/ccMacros.h"
- #include "base/ccCArray.h"
- #include "base/uthash.h"
- NS_CC_BEGIN
- //
- // singleton stuff
- //
- typedef struct _hashElement
- {
- struct _ccArray *actions;
- Node *target;
- int actionIndex;
- Action *currentAction;
- bool currentActionSalvaged;
- bool paused;
- UT_hash_handle hh;
- } tHashElement;
- ActionManager::ActionManager()
- : _targets(nullptr),
- _currentTarget(nullptr),
- _currentTargetSalvaged(false)
- {
- }
- ActionManager::~ActionManager()
- {
- CCLOGINFO("deallocing ActionManager: %p", this);
- removeAllActions();
- }
- // private
- void ActionManager::deleteHashElement(tHashElement *element)
- {
- ccArrayFree(element->actions);
- HASH_DEL(_targets, element);
- element->target->release();
- free(element);
- }
- void ActionManager::actionAllocWithHashElement(tHashElement *element)
- {
- // 4 actions per Node by default
- if (element->actions == nullptr)
- {
- element->actions = ccArrayNew(4);
- }else
- if (element->actions->num == element->actions->max)
- {
- ccArrayDoubleCapacity(element->actions);
- }
- }
- void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element)
- {
- Action *action = static_cast<Action*>(element->actions->arr[index]);
- if (action == element->currentAction && (! element->currentActionSalvaged))
- {
- element->currentAction->retain();
- element->currentActionSalvaged = true;
- }
- ccArrayRemoveObjectAtIndex(element->actions, index, true);
- // update actionIndex in case we are in tick. looping over the actions
- if (element->actionIndex >= index)
- {
- element->actionIndex--;
- }
- if (element->actions->num == 0)
- {
- if (_currentTarget == element)
- {
- _currentTargetSalvaged = true;
- }
- else
- {
- deleteHashElement(element);
- }
- }
- }
- // pause / resume
- void ActionManager::pauseTarget(Node *target)
- {
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- element->paused = true;
- }
- }
- void ActionManager::resumeTarget(Node *target)
- {
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- element->paused = false;
- }
- }
- Vector<Node*> ActionManager::pauseAllRunningActions()
- {
- Vector<Node*> idsWithActions;
-
- for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next)
- {
- if (! element->paused)
- {
- element->paused = true;
- idsWithActions.pushBack(element->target);
- }
- }
-
- return idsWithActions;
- }
- void ActionManager::resumeTargets(const Vector<Node*>& targetsToResume)
- {
- for(const auto &node : targetsToResume)
- {
- this->resumeTarget(node);
- }
- }
- // run
- void ActionManager::addAction(Action *action, Node *target, bool paused)
- {
- CCASSERT(action != nullptr, "action can't be nullptr!");
- CCASSERT(target != nullptr, "target can't be nullptr!");
- if(action == nullptr || target == nullptr)
- return;
- tHashElement *element = nullptr;
- // we should convert it to Ref*, because we save it as Ref*
- Ref *tmp = target;
- HASH_FIND_PTR(_targets, &tmp, element);
- if (! element)
- {
- element = (tHashElement*)calloc(sizeof(*element), 1);
- element->paused = paused;
- target->retain();
- element->target = target;
- HASH_ADD_PTR(_targets, target, element);
- }
- actionAllocWithHashElement(element);
-
- CCASSERT(! ccArrayContainsObject(element->actions, action), "action already be added!");
- ccArrayAppendObject(element->actions, action);
-
- action->startWithTarget(target);
- }
- // remove
- void ActionManager::removeAllActions()
- {
- for (tHashElement *element = _targets; element != nullptr; )
- {
- auto target = element->target;
- element = (tHashElement*)element->hh.next;
- removeAllActionsFromTarget(target);
- }
- }
- void ActionManager::removeAllActionsFromTarget(Node *target)
- {
- // explicit null handling
- if (target == nullptr)
- {
- return;
- }
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))
- {
- element->currentAction->retain();
- element->currentActionSalvaged = true;
- }
- ccArrayRemoveAllObjects(element->actions);
- if (_currentTarget == element)
- {
- _currentTargetSalvaged = true;
- }
- else
- {
- deleteHashElement(element);
- }
- }
- }
- void ActionManager::removeAction(Action *action)
- {
- // explicit null handling
- if (action == nullptr)
- {
- return;
- }
- tHashElement *element = nullptr;
- Ref *target = action->getOriginalTarget();
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- auto i = ccArrayGetIndexOfObject(element->actions, action);
- if (i != CC_INVALID_INDEX)
- {
- removeActionAtIndex(i, element);
- }
- }
- }
- void ActionManager::removeActionByTag(int tag, Node *target)
- {
- CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
- CCASSERT(target != nullptr, "target can't be nullptr!");
- if (target == nullptr)
- {
- return;
- }
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- auto limit = element->actions->num;
- for (int i = 0; i < limit; ++i)
- {
- Action *action = static_cast<Action*>(element->actions->arr[i]);
- if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
- {
- removeActionAtIndex(i, element);
- break;
- }
- }
- }
- }
- void ActionManager::removeAllActionsByTag(int tag, Node *target)
- {
- CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
- CCASSERT(target != nullptr, "target can't be nullptr!");
- if (target == nullptr)
- {
- return;
- }
-
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
-
- if (element)
- {
- auto limit = element->actions->num;
- for (int i = 0; i < limit;)
- {
- Action *action = static_cast<Action*>(element->actions->arr[i]);
- if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
- {
- removeActionAtIndex(i, element);
- --limit;
- }
- else
- {
- ++i;
- }
- }
- }
- }
- void ActionManager::removeActionsByFlags(unsigned int flags, Node *target)
- {
- if (flags == 0)
- {
- return;
- }
- CCASSERT(target != nullptr, "target can't be nullptr!");
- if (target == nullptr)
- {
- return;
- }
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- auto limit = element->actions->num;
- for (int i = 0; i < limit;)
- {
- Action *action = static_cast<Action*>(element->actions->arr[i]);
- if ((action->getFlags() & flags) != 0 && action->getOriginalTarget() == target)
- {
- removeActionAtIndex(i, element);
- --limit;
- }
- else
- {
- ++i;
- }
- }
- }
- }
- // get
- // FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
- // and, it is not possible to get the address of a reference
- Action* ActionManager::getActionByTag(int tag, const Node *target) const
- {
- CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- if (element->actions != nullptr)
- {
- auto limit = element->actions->num;
- for (int i = 0; i < limit; ++i)
- {
- Action *action = static_cast<Action*>(element->actions->arr[i]);
- if (action->getTag() == (int)tag)
- {
- return action;
- }
- }
- }
- }
- return nullptr;
- }
- // FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
- // and, it is not possible to get the address of a reference
- ssize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const
- {
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if (element)
- {
- return element->actions ? element->actions->num : 0;
- }
- return 0;
- }
- // FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
- // and, it is not possible to get the address of a reference
- size_t ActionManager::getNumberOfRunningActionsInTargetByTag(const Node *target,
- int tag)
- {
- CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
- tHashElement *element = nullptr;
- HASH_FIND_PTR(_targets, &target, element);
- if(!element || !element->actions)
- return 0;
- int count = 0;
- auto limit = element->actions->num;
- for(int i = 0; i < limit; ++i)
- {
- auto action = static_cast<Action*>(element->actions->arr[i]);
- if(action->getTag() == tag)
- ++count;
- }
- return count;
- }
- ssize_t ActionManager::getNumberOfRunningActions() const
- {
- ssize_t count = 0;
- struct _hashElement* element = nullptr;
- struct _hashElement* tmp = nullptr;
- HASH_ITER(hh, _targets, element, tmp)
- {
- count += (element->actions ? element->actions->num : 0);
- }
- return count;
- }
- // main loop
- void ActionManager::update(float dt)
- {
- for (tHashElement *elt = _targets; elt != nullptr; )
- {
- _currentTarget = elt;
- _currentTargetSalvaged = false;
- if (! _currentTarget->paused)
- {
- // The 'actions' MutableArray may change while inside this loop.
- for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
- _currentTarget->actionIndex++)
- {
- _currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
- if (_currentTarget->currentAction == nullptr)
- {
- continue;
- }
- _currentTarget->currentActionSalvaged = false;
- _currentTarget->currentAction->step(dt);
- if (_currentTarget->currentActionSalvaged)
- {
- // The currentAction told the node to remove it. To prevent the action from
- // accidentally deallocating itself before finishing its step, we retained
- // it. Now that step is done, it's safe to release it.
- _currentTarget->currentAction->release();
- } else
- if (_currentTarget->currentAction->isDone())
- {
- _currentTarget->currentAction->stop();
- Action *action = _currentTarget->currentAction;
- // Make currentAction nil to prevent removeAction from salvaging it.
- _currentTarget->currentAction = nullptr;
- removeAction(action);
- }
- _currentTarget->currentAction = nullptr;
- }
- }
- // elt, at this moment, is still valid
- // so it is safe to ask this here (issue #490)
- elt = (tHashElement*)(elt->hh.next);
- // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
- if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
- {
- deleteHashElement(_currentTarget);
- }
- //if some node reference 'target', it's reference count >= 2 (issues #14050)
- else if (_currentTarget->target->getReferenceCount() == 1)
- {
- deleteHashElement(_currentTarget);
- }
- }
- // issue #635
- _currentTarget = nullptr;
- }
- NS_CC_END
|