• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

OTClient Effects frame duration speed

Cesrarr

Member
Joined
Oct 3, 2018
Messages
34
Reaction score
7
I see that some effects have different minimum and maximum duration speed, i tried change those values to others, but when i login in game, the speed dont changes, i can put 0 or 500 in all frames and will have the same value.

My dat is compiled with [x] improved animations, the min/max duration in items are working good, but in effects dont, do i need compile otclient with something on?

2018-11-04_18-57-38.gif
 
Last edited:
2018-11-06_16-37-27.gif

through source edit i could set effect id and frame duration,
effect 10 = 200ms
effect 11 = 400ms; etc

but would be better if it works like items where you can set duration for each frame in object builder, there you can set independent frame time for effects
 
Last edited:
Enchanced animations are enabled for client > 10.50 (load then from .dat):
PHP:
if(version >= 1050) {
    enableFeature(Otc::GameEnhancedAnimations);
}

They are loaded into m_animator of Thing (Effect is also a Thing in OTClient):
PHP:
if(groupAnimationsPhases > 1 && g_game.getFeature(Otc::GameEnhancedAnimations)) {
    m_animator = AnimatorPtr(new Animator);
    m_animator->unserialize(groupAnimationsPhases, fin);
}

Animator is used in item animation:
PHP:
int Item::calculateAnimationPhase(bool animate)
{
    if(getAnimationPhases() > 1) {
        if(animate) {
            if(getAnimator() != nullptr)
                return getAnimator()->getPhase();

but for unknown reason is totally ignored by Effect:
PHP:
void Effect::onAppear()
{
    m_animationTimer.restart();
    m_phaseDuration = EFFECT_TICKS_PER_FRAME;

Effect uses constant animation speed.

To make .dat attribute works. You need to rewrite effect.cpp to calculate animation phase using m_animator.

EDIT:
Important information!
There is one Animator for all items with same ID. In case of Effect you need to clone Animator or clone phases and their duration from Animator, because all Effects on screen can't be synchronized [show same frame of animation].
 
Last edited:
Thank you @Gesior.pl, but i already had found it all, im trying just what you said rewrite effect.cpp



=====

What i've done:

effect.h
under uint16 m_id; added
Code:
    bool m_async;
    uint8 m_phase;
    ticks_t m_lastPhase;

effect.cpp

under #include <framework/core/eventdispatcher.h> added
Code:
#include "thing.h"

Effect::Effect() :
    m_async(true),
    m_phase(0),
    m_lastPhase(0)


changed

Code:
void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int offsetX, int offsetY, LightView *lightView)
{
    if(m_id == 0)
        return;

    int animationPhase = 0;
    if(animate)
        animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / m_phaseDuration), getAnimationPhases() - 1);

    int xPattern = offsetX % getNumPatternX();
    if(xPattern < 0)
        xPattern += getNumPatternX();

    int yPattern = offsetY % getNumPatternY();
    if(yPattern < 0)
        yPattern += getNumPatternY();

    rawGetThingType()->draw(dest, scaleFactor, 0, xPattern, yPattern, 0, animationPhase, lightView);
}

to

Code:
void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int offsetX, int offsetY, LightView *lightView)
{
    if(m_id == 0)
        return;

    int animationPhase = calculateAnimationPhase(animate);

    int xPattern = offsetX % getNumPatternX();
    if(xPattern < 0)
        xPattern += getNumPatternX();

    int yPattern = offsetY % getNumPatternY();
    if(yPattern < 0)
        yPattern += getNumPatternY();

    rawGetThingType()->draw(dest, scaleFactor, 0, xPattern, yPattern, 0, animationPhase, lightView);
}


before void Effect: onAppear() added

Code:
int Effect::calculateAnimationPhase(bool animate)
{
    if(getAnimationPhases() > 1) {
        if(animate) {
            if(getAnimator() != nullptr)
                return getAnimator()->getPhase();

            if(m_async)
                return (g_clock.millis() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME;
            else {
                if(g_clock.millis() - m_lastPhase >= Otc::ITEM_TICKS_PER_FRAME) {
                    m_phase = (m_phase + 1) % getAnimationPhases();
                    m_lastPhase = g_clock.millis();
                }
                return m_phase;
            }
        } else
            return getAnimationPhases()-1;
    }
    return 0;
}



My doubt is just how to edit in effect.cpp to make animation phase and time work with new code
Code:
void Effect::onAppear()
{
    m_animationTimer.restart();
    m_phaseDuration = ?????????;
 
Last edited:
Problem is:
PHP:
if(getAnimator() != nullptr)
    return getAnimator()->getPhase();
getAnimator() is defined in thing.h:
PHP:
AnimatorPtr getAnimator() { return rawGetThingType()->getAnimator(); }
Which means, it will take Animator from rawGetThingType(), which is defined in effect.cpp:
PHP:
ThingType *Effect::rawGetThingType()
{
    return g_things.rawGetThingType(m_id, ThingCategoryEffect);
}
Which means, it will return same Animator object for all Effects with same m_id (effect id).

1. I would define 'AnimatorPtr m_effectAnimator' in effect.h
2. In Effect::eek:nAppear() I would clone Animator from getAnimator() to m_effectAnimator . We got AnimatorPtr, which contains inside pointer to Animator, a bit weird for me, I dont' knowe how to copy it 'deep'.
3. Then you need to reset values of m_effectAnimator clone (maybe call resetAnimation of Animator?).
4. Important part of 'effect' is that it disappears after animation. Now there is a code in onAppear:
PHP:
// schedule removal
auto self = asEffect();
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_phaseDuration * getAnimationPhases());
Which uses constant animation speed and count frames: m_phaseDuration * getAnimationPhases()
Now you got to calculate time of Animator (sum all 'phaseDurations' ? ) and set valid time to remove effect from game.

-------------------------
If you plan to make your code public, you should not use Animator (just copy some of it's values to new variables in effect.h) and put 'something like animator code' in effect.cpp
Animator
is designed to show infinite animations of items, loops in forward/backward order. Effect play all frames of animation only once.
You should also remember that OTClient must support old clients with simple effect animations (no animator).
 
Problem is:
PHP:
if(getAnimator() != nullptr)
    return getAnimator()->getPhase();
getAnimator() is defined in thing.h:
PHP:
AnimatorPtr getAnimator() { return rawGetThingType()->getAnimator(); }

Thats why i used #include "thing.h" in effect.cpp

PHP:
ThingType *Effect::rawGetThingType()
{
    return g_things.rawGetThingType(m_id, ThingCategoryEffect);
}
Which means, it will return same Animator object for all Effects with same m_id (effect id).

This is the reason that effects don't use pre defined time to frames like items, now the point is how to solve the problem of

" same Animator object for all Effects with same m_id (effect id)"


I already got effects in game reading time from object builder, now i have two problems.
1- desynchronize effects
2- make effects disappear properly



Edit:
problem 2 solved.

Effects running using timers declared in object builder and disappearing properly , now just need find a way to desynchronize they and make it start from initial frame when used again.
 
Last edited:
Thats why i used #include "thing.h" in effect.cpp

I already got effects in game reading time from object builder, now i have two problems.
1- desynchronize effects
2- make effects disappear properly

Edit:
2
- problem 2 solved.

Effects running using timers declared in object builder and disappearing properly , now just need find a way to desynchronize they and make it start from initial frame when used again.
Show your current effect.h and effect.cpp
 
@Gesior.pl
Changes were made in effect.cpp effect.h animator.cpp animator.h

If you can use any tool like notepad++ search for //mod animação
Will highlights my mods


notepad++_2018-11-08_12-16-50.png


====
Its a weird thing of otclient, you can compile a client within a clean source and test, add this image as an item.

effect_801.png
I set frame 1 duration to 1seg, frame 2 = 2seg, frame 3 = 3seg:

Each time i create a new item, instead it start from frame 1, it start from whatever frame running atm
2018-11-08_15-35-42.gif

This is the reason effects dont read time for each frame, if i cast a effect of 10 frames and when it be in frame 5 : another player cast same effect, instead the effect start from initial frame it will start from frame 5.

You can see in those ots like pxg, all animated items, animate at same time, same frame, their animations are not independent, you can take one in your bag and when you put it on ground again, it will copy the frame running on screen atm instead start from 0.

The code to effects read time from editor is done, now the problem is correct this default feature in otclient, solving it also can make items animations independent of others with same id.

Scan: VirusTotal
 

Attachments

Last edited:
Back
Top