2011-03-19 10:34:26 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2008-2009 Jason Booth
|
2014-01-07 11:25:07 +08:00
|
|
|
Copyright (c) 2010-2012 cocos2d-x.org
|
2018-01-29 16:25:32 +08:00
|
|
|
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
2011-03-19 10:34:26 +08:00
|
|
|
|
2022-08-08 18:02:17 +08:00
|
|
|
https://axys1.github.io/
|
2011-03-19 10:34:26 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2010-08-10 14:02:13 +08:00
|
|
|
/*
|
|
|
|
* Elastic, Back and Bounce actions based on code from:
|
|
|
|
* http://github.com/NikhilK/silverlightfx/
|
|
|
|
*
|
|
|
|
* by http://github.com/NikhilK
|
|
|
|
*/
|
2011-03-19 10:34:26 +08:00
|
|
|
|
2014-04-27 01:11:22 +08:00
|
|
|
#include "2d/CCActionEase.h"
|
|
|
|
#include "2d/CCTweenFunction.h"
|
2011-03-19 10:34:26 +08:00
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_BEGIN
|
2010-08-10 14:02:13 +08:00
|
|
|
|
|
|
|
#ifndef M_PI_X_2
|
2021-12-25 10:04:45 +08:00
|
|
|
# define M_PI_X_2 (float)M_PI * 2.0f
|
2010-08-10 14:02:13 +08:00
|
|
|
#endif
|
|
|
|
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
|
|
|
// EaseAction
|
|
|
|
//
|
2012-06-14 15:13:16 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
bool ActionEase::initWithAction(ActionInterval* action)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(action != nullptr, "action couldn't be nullptr!");
|
2016-04-08 13:40:36 +08:00
|
|
|
if (action == nullptr)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-04-19 10:46:37 +08:00
|
|
|
|
2013-11-16 21:08:00 +08:00
|
|
|
if (ActionInterval::initWithDuration(action->getDuration()))
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
2013-11-16 21:08:00 +08:00
|
|
|
_inner = action;
|
|
|
|
action->retain();
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
return true;
|
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
return false;
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2019-07-22 09:38:46 +08:00
|
|
|
ActionEase::~ActionEase()
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2022-07-15 19:17:01 +08:00
|
|
|
AX_SAFE_RELEASE(_inner);
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void ActionEase::startWithTarget(Node* target)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2016-04-08 13:40:36 +08:00
|
|
|
if (target && _inner)
|
|
|
|
{
|
|
|
|
ActionInterval::startWithTarget(target);
|
|
|
|
_inner->startWithTarget(_target);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-08 18:02:17 +08:00
|
|
|
ax::log("ActionEase::startWithTarget error: target or _inner is nullptr!");
|
2016-04-08 13:40:36 +08:00
|
|
|
}
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2019-07-22 09:38:46 +08:00
|
|
|
void ActionEase::stop()
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2016-04-08 13:40:36 +08:00
|
|
|
if (_inner)
|
|
|
|
_inner->stop();
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
ActionInterval::stop();
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
void ActionEase::update(float time)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2013-06-15 14:03:30 +08:00
|
|
|
_inner->update(time);
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
ActionInterval* ActionEase::getInnerAction()
|
2013-03-01 14:08:23 +08:00
|
|
|
{
|
2013-06-15 14:03:30 +08:00
|
|
|
return _inner;
|
2013-03-01 14:08:23 +08:00
|
|
|
}
|
|
|
|
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
|
|
|
// EaseRateAction
|
|
|
|
//
|
2012-06-14 15:13:16 +08:00
|
|
|
|
2015-04-24 18:31:44 +08:00
|
|
|
EaseRateAction* EaseRateAction::create(ActionInterval* action, float rate)
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(action != nullptr, "action cannot be nullptr!");
|
2016-09-05 10:18:30 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
EaseRateAction* easeRateAction = new EaseRateAction();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (easeRateAction->initWithAction(action, rate))
|
2015-04-24 18:31:44 +08:00
|
|
|
{
|
2016-04-08 13:40:36 +08:00
|
|
|
easeRateAction->autorelease();
|
|
|
|
return easeRateAction;
|
2015-04-24 18:31:44 +08:00
|
|
|
}
|
2016-09-05 10:18:30 +08:00
|
|
|
|
2022-07-15 19:17:01 +08:00
|
|
|
AX_SAFE_DELETE(easeRateAction);
|
2016-04-08 13:40:36 +08:00
|
|
|
return nullptr;
|
2015-04-24 18:31:44 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
bool EaseRateAction::initWithAction(ActionInterval* action, float rate)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2013-11-16 21:08:00 +08:00
|
|
|
if (ActionEase::initWithAction(action))
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
2013-11-16 21:08:00 +08:00
|
|
|
_rate = rate;
|
2012-04-19 14:35:52 +08:00
|
|
|
return true;
|
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
return false;
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2016-09-05 10:18:30 +08:00
|
|
|
// NOTE: Converting these macros into Templates is desirable, but please see
|
|
|
|
// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
|
|
|
|
//
|
2021-12-25 10:04:45 +08:00
|
|
|
#define EASE_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC, REVERSE_CLASSNAME) \
|
2022-08-08 18:02:17 +08:00
|
|
|
CLASSNAME* CLASSNAME::create(ax::ActionInterval* action) \
|
2021-12-25 10:04:45 +08:00
|
|
|
{ \
|
|
|
|
CLASSNAME* ease = new CLASSNAME(); \
|
|
|
|
if (ease->initWithAction(action)) \
|
|
|
|
ease->autorelease(); \
|
|
|
|
else \
|
2022-07-15 19:17:01 +08:00
|
|
|
AX_SAFE_DELETE(ease); \
|
2021-12-25 10:04:45 +08:00
|
|
|
return ease; \
|
|
|
|
} \
|
|
|
|
CLASSNAME* CLASSNAME::clone() const \
|
|
|
|
{ \
|
|
|
|
if (_inner) \
|
|
|
|
return CLASSNAME::create(_inner->clone()); \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
void CLASSNAME::update(float time) { _inner->update(TWEEN_FUNC(time)); } \
|
|
|
|
ActionEase* CLASSNAME::reverse() const { return REVERSE_CLASSNAME::create(_inner->reverse()); }
|
2016-09-05 10:18:30 +08:00
|
|
|
|
|
|
|
EASE_TEMPLATE_IMPL(EaseExponentialIn, tweenfunc::expoEaseIn, EaseExponentialOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseExponentialOut, tweenfunc::expoEaseOut, EaseExponentialIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseExponentialInOut, tweenfunc::expoEaseInOut, EaseExponentialInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseSineIn, tweenfunc::sineEaseIn, EaseSineOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseSineOut, tweenfunc::sineEaseOut, EaseSineIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseSineInOut, tweenfunc::sineEaseInOut, EaseSineInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBounceIn, tweenfunc::bounceEaseIn, EaseBounceOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBounceOut, tweenfunc::bounceEaseOut, EaseBounceIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBounceInOut, tweenfunc::bounceEaseInOut, EaseBounceInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBackIn, tweenfunc::backEaseIn, EaseBackOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBackOut, tweenfunc::backEaseOut, EaseBackIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseBackInOut, tweenfunc::backEaseInOut, EaseBackInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuadraticActionIn, tweenfunc::quadraticIn, EaseQuadraticActionIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuadraticActionOut, tweenfunc::quadraticOut, EaseQuadraticActionOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuadraticActionInOut, tweenfunc::quadraticInOut, EaseQuadraticActionInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuarticActionIn, tweenfunc::quartEaseIn, EaseQuarticActionIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuarticActionOut, tweenfunc::quartEaseOut, EaseQuarticActionOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuarticActionInOut, tweenfunc::quartEaseInOut, EaseQuarticActionInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuinticActionIn, tweenfunc::quintEaseIn, EaseQuinticActionIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuinticActionOut, tweenfunc::quintEaseOut, EaseQuinticActionOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseQuinticActionInOut, tweenfunc::quintEaseInOut, EaseQuinticActionInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCircleActionIn, tweenfunc::circEaseIn, EaseCircleActionIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCircleActionOut, tweenfunc::circEaseOut, EaseCircleActionOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCircleActionInOut, tweenfunc::circEaseInOut, EaseCircleActionInOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCubicActionIn, tweenfunc::cubicEaseIn, EaseCubicActionIn);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCubicActionOut, tweenfunc::cubicEaseOut, EaseCubicActionOut);
|
|
|
|
EASE_TEMPLATE_IMPL(EaseCubicActionInOut, tweenfunc::cubicEaseInOut, EaseCubicActionInOut);
|
|
|
|
|
|
|
|
//
|
|
|
|
// NOTE: Converting these macros into Templates is desirable, but please see
|
|
|
|
// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
|
|
|
|
//
|
2021-12-25 10:04:45 +08:00
|
|
|
#define EASERATE_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC) \
|
2022-08-08 18:02:17 +08:00
|
|
|
CLASSNAME* CLASSNAME::create(ax::ActionInterval* action, float rate) \
|
2021-12-25 10:04:45 +08:00
|
|
|
{ \
|
|
|
|
CLASSNAME* ease = new CLASSNAME(); \
|
|
|
|
if (ease->initWithAction(action, rate)) \
|
|
|
|
ease->autorelease(); \
|
|
|
|
else \
|
2022-07-15 19:17:01 +08:00
|
|
|
AX_SAFE_DELETE(ease); \
|
2021-12-25 10:04:45 +08:00
|
|
|
return ease; \
|
|
|
|
} \
|
|
|
|
CLASSNAME* CLASSNAME::clone() const \
|
|
|
|
{ \
|
|
|
|
if (_inner) \
|
|
|
|
return CLASSNAME::create(_inner->clone(), _rate); \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
void CLASSNAME::update(float time) { _inner->update(TWEEN_FUNC(time, _rate)); } \
|
|
|
|
EaseRateAction* CLASSNAME::reverse() const { return CLASSNAME::create(_inner->reverse(), 1.f / _rate); }
|
2016-09-05 10:18:30 +08:00
|
|
|
|
|
|
|
// NOTE: the original code used the same class for the `reverse()` method
|
|
|
|
EASERATE_TEMPLATE_IMPL(EaseIn, tweenfunc::easeIn);
|
|
|
|
EASERATE_TEMPLATE_IMPL(EaseOut, tweenfunc::easeOut);
|
|
|
|
EASERATE_TEMPLATE_IMPL(EaseInOut, tweenfunc::easeInOut);
|
2010-08-25 18:31:55 +08:00
|
|
|
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2016-09-05 10:18:30 +08:00
|
|
|
// EaseElastic
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2012-06-14 15:13:16 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
bool EaseElastic::initWithAction(ActionInterval* action, float period /* = 0.3f*/)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2016-09-05 10:18:30 +08:00
|
|
|
if (ActionEase::initWithAction(action))
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
2016-09-05 10:18:30 +08:00
|
|
|
_period = period;
|
|
|
|
return true;
|
2012-04-19 14:35:52 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2016-09-05 10:18:30 +08:00
|
|
|
return false;
|
2012-05-04 16:22:50 +08:00
|
|
|
}
|
|
|
|
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2016-09-05 10:18:30 +08:00
|
|
|
// NOTE: Converting these macros into Templates is desirable, but please see
|
|
|
|
// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2021-12-25 10:04:45 +08:00
|
|
|
#define EASEELASTIC_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC, REVERSE_CLASSNAME) \
|
2022-08-08 18:02:17 +08:00
|
|
|
CLASSNAME* CLASSNAME::create(ax::ActionInterval* action, float period /* = 0.3f*/) \
|
2021-12-25 10:04:45 +08:00
|
|
|
{ \
|
|
|
|
CLASSNAME* ease = new CLASSNAME(); \
|
|
|
|
if (ease->initWithAction(action, period)) \
|
|
|
|
ease->autorelease(); \
|
|
|
|
else \
|
2022-07-15 19:17:01 +08:00
|
|
|
AX_SAFE_DELETE(ease); \
|
2021-12-25 10:04:45 +08:00
|
|
|
return ease; \
|
|
|
|
} \
|
|
|
|
CLASSNAME* CLASSNAME::clone() const \
|
|
|
|
{ \
|
|
|
|
if (_inner) \
|
|
|
|
return CLASSNAME::create(_inner->clone(), _period); \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
void CLASSNAME::update(float time) { _inner->update(TWEEN_FUNC(time, _period)); } \
|
|
|
|
EaseElastic* CLASSNAME::reverse() const { return REVERSE_CLASSNAME::create(_inner->reverse(), _period); }
|
2012-04-19 10:46:37 +08:00
|
|
|
|
2016-09-05 10:18:30 +08:00
|
|
|
EASEELASTIC_TEMPLATE_IMPL(EaseElasticIn, tweenfunc::elasticEaseIn, EaseElasticOut);
|
|
|
|
EASEELASTIC_TEMPLATE_IMPL(EaseElasticOut, tweenfunc::elasticEaseOut, EaseElasticIn);
|
|
|
|
EASEELASTIC_TEMPLATE_IMPL(EaseElasticInOut, tweenfunc::elasticEaseInOut, EaseElasticInOut);
|
2012-04-19 10:46:37 +08:00
|
|
|
|
|
|
|
//
|
2016-09-05 10:18:30 +08:00
|
|
|
// EaseBezierAction
|
2012-04-19 10:46:37 +08:00
|
|
|
//
|
2012-06-14 15:13:16 +08:00
|
|
|
|
2022-08-08 18:02:17 +08:00
|
|
|
EaseBezierAction* EaseBezierAction::create(ax::ActionInterval* action)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
EaseBezierAction* ret = new EaseBezierAction();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (ret->initWithAction(action))
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
2016-04-08 13:40:36 +08:00
|
|
|
ret->autorelease();
|
|
|
|
return ret;
|
2012-04-19 14:35:52 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2016-04-08 13:40:36 +08:00
|
|
|
delete ret;
|
|
|
|
return nullptr;
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void EaseBezierAction::setBezierParamer(float p0, float p1, float p2, float p3)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2016-09-05 10:18:30 +08:00
|
|
|
_p0 = p0;
|
|
|
|
_p1 = p1;
|
|
|
|
_p2 = p2;
|
|
|
|
_p3 = p3;
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-05 10:18:30 +08:00
|
|
|
EaseBezierAction* EaseBezierAction::clone() const
|
2013-06-14 08:25:14 +08:00
|
|
|
{
|
2015-01-08 10:22:45 +08:00
|
|
|
// no copy constructor
|
2016-04-08 13:40:36 +08:00
|
|
|
if (_inner)
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
2016-09-05 10:18:30 +08:00
|
|
|
auto ret = EaseBezierAction::create(_inner->clone());
|
|
|
|
if (ret)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
ret->setBezierParamer(_p0, _p1, _p2, _p3);
|
2016-09-05 10:18:30 +08:00
|
|
|
}
|
2016-04-08 13:40:36 +08:00
|
|
|
return ret;
|
2012-04-19 14:35:52 +08:00
|
|
|
}
|
2010-08-10 14:02:13 +08:00
|
|
|
|
2016-04-08 13:40:36 +08:00
|
|
|
return nullptr;
|
2013-06-14 08:25:14 +08:00
|
|
|
}
|
|
|
|
|
2016-09-05 10:18:30 +08:00
|
|
|
void EaseBezierAction::update(float time)
|
2012-04-19 10:46:37 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_inner->update(tweenfunc::bezieratFunction(_p0, _p1, _p2, _p3, time));
|
2012-04-19 10:46:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-05 10:18:30 +08:00
|
|
|
EaseBezierAction* EaseBezierAction::reverse() const
|
2012-11-02 10:06:48 +08:00
|
|
|
{
|
2016-09-05 10:18:30 +08:00
|
|
|
EaseBezierAction* reverseAction = EaseBezierAction::create(_inner->reverse());
|
2021-12-25 10:04:45 +08:00
|
|
|
reverseAction->setBezierParamer(_p3, _p2, _p1, _p0);
|
2016-09-05 10:18:30 +08:00
|
|
|
return reverseAction;
|
2014-02-17 11:20:01 +08:00
|
|
|
}
|
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_END
|