sergebaz

Прославленный
Местный
Сообщения
509
Розыгрыши
0
Решения
1
Репутация
230
Реакции
99
Баллы
1 443
Интересует механика урона птс сервера. Интересуют формулы:
1. нанесение физ дамага автоатакой, причем расчет бьющий и цель, бьющий: п атака; бафы\танцы\сонги;бижа,броня(статы: дех сон стрейт),урон стихией,критическая атака,пвп заточка оружия и др бонусы са, бетл форсы(п атака) от участников пати,клан скилы они же п атака , цель бьющего (моб или плеер) п деф бафы\ сонги \танцы резист к оружию(мобы) и стихии, пасивка от клан скилов, тип брони и тд.
2.нанесение маг атакой скилами и так же сам бьющий и цель: маг атакующий м атака, заточка скилов, бафы , криты, стихия ослабление ( пример на стихию ветра у сх), бонусы пвп от заточки и са в оружии; цель мага (моб или плеер) м деф, бижа, резисты к стихиям, бафы сонги танцы, резисты к стихиям клана. Можно в любом виде, буду благодарен всей инфе по этим формулам !
 

sergebaz там нету справочника формул. Все нужно смотреть в ИДЕ, уже много раз писалось об этом. Все формулы размыты, и находятся в самых неожиданных местах.

Я не понимаю, почему бы просто не открыть ИДУ, ведь тут же все написано, даже довольно java-подобно. Вот начало декомпила по урону прям с ИДЫ, не портированного в с++:

C++:
  SEHCounter = 0;
  creature = CSmartID::GetCreature(&this->m_b.m_actorSID);
  if ( !creature )
  {
    return 0;
  }
  attacker = CSmartID::GetCreature(&attackerSID);
  if ( !attacker )
  {
    return 0;
  }
  m_skillInfo = this->m_skillInfo;
  if ( m_skillInfo && !m_skillInfo->m_magicType )
  {
    cancelled = false;
  }
  sharedData = creature->m_d.m_sharedData;
  menStat = sharedData->menStat;
  baseHP = HIDWORD(sharedData->baseHP);
  LODWORD(baseHP2) = LODWORD(sharedData->baseHP);
  HIDWORD(baseHP2) = baseHP;
  LOBYTE(SEHCounter) = 1;
  damage = (damage * creature->m_d.m_cancelDelayPercentBonus + creature->m_d.m_cancelDelayDiffBonus);
  creatureLevel = CWorldCreature::GetStat(creature, 0);// level
  attackerLevel = CWorldCreature::GetStat(attacker, 0);
  damageImpact = damage / baseHP2;
  if ( menStat * 0.125 + creatureLevel - attackerLevel < 100.0 * damageImpact )
  {
    cancelChance = (damageImpact * 200.0);
    if ( cancelChance > 98 )
    {
      cancelChance = 98;
    }
  }
  else
  {
    cancelChance = 5;
  }
  SEHCounter = 0;
  LOBYTE(SEHCounter) = 3;
  if ( cancelChance < Utils::Random(100u) || this->m_cancelRelated > 0 )
  {
    cancelled = false;
  }
  else
  {
    cancelled = true;
  }
  creatureController = creature->m_d.m_creatureController;
  LOBYTE(SEHCounter) = 4;
  CCreatureController::ActionBroadcastSkillCanceled(creatureController, this);
  if ( !creature->vtable->m_v.m_v.base.IsUser(creature) )
  {
    return 0;
  }
  user = creature->vtable->m_v.CastToUser(creature);
  if (cancelled)
  {
    CServerSocketBase::Send(user->m_d.m_userSocket, "cdd", ServerToClientPacket_SystemMessage, SystemMessageId_Casting_has_been_interrupted, 0);
  ...

Функция по обработке скилла - на пару строк кода, итерации по коллекции. Надо понимать, что в ПТС со скиллами по-проще, чем в L2J. Все скиллы по тику просто изменяют pre/post diff/percent/abs значения, которые учавтствую в формулах

Все формулы до безобразия просты, типа

C++:
    m_sharedData->magDef = menBonus * levelBonus * ((double)m_sharedData->equipedArmorMDefend + m_magDefDiffPreBonus) * m_magDefPercentBonus + m_magDefDiffPostBonus;

Если уж очень хочется 1 к 1му, то я использую такие вот тесты (на C#), где проверяю все формулы

C#:
        public static IEnumerable<object[]> FirstClassChangeParamsData =>
            new List<object[]>
            {
                new object[] { ClassType.Human_Fighter, ClassType.Knight, ClassParamsExpectations.KnightExpectation()},
                new object[] { ClassType.Orc_Fighter, ClassType.Orc_Monk, ClassParamsExpectations.OrcMonkExpectation()},
                new object[] { ClassType.Orc_Fighter, ClassType.Orc_Raider, ClassParamsExpectations.OrcRaiderExpectation()},
                new object[] { ClassType.Orc_Mage, ClassType.Orc_Shaman, ClassParamsExpectations.OrcShamanExpectation()},
                new object[] { ClassType.Dwarven_Fighter, ClassType.Scavenger, ClassParamsExpectations.ScavengerExpectation()},
                new object[] { ClassType.Dwarven_Fighter, ClassType.Artisan, ClassParamsExpectations.ArtisanExpectation()},
                // ...
            };

        [Theory]
        [MemberData(nameof(FirstClassChangeParamsData))]
        public void FirstClassChangeParamsCheck(ClassType originalClassType, ClassType classType, ClassParamsExpectations expectedParams)
        {
            CacheDAdmin admin = new CacheDAdmin("admin");
            admin.ConnectToService(ConnectionConfig.LoginServerIP, ConnectionConfig.CacheAdminPort);

            Usecases.CacheAdmin.CharUseCases adminUC = new Usecases.CacheAdmin.CharUseCases(admin);
            adminUC.DeleteChar("bowTst");

            PcInstance player = new PcInstance();
            LobbyScenario scenario = new LobbyScenario(player, ConnectionConfig.LoginServerIP, ConnectionConfig.LoginServerPort);
            Assert.True(scenario.EnterLobby("test1", "test1"));
            Assert.True(scenario.CreateChar("bowTst", originalClassType));
            Assert.True(scenario.EnterWorld());

            BuilderCommandUseCase builder = new BuilderCommandUseCase(player);
            builder.SetLevel(20);
            builder.ChangeClass(classType);
            player.ConsumeUpdates();

            builder.AddAllSkills();
            player.ConsumeUpdates();
            player.ConsumeStatUpdates();

            UserInfoInd info = player.UserInfo;

            Assert.Equal(expectedParams.ClassType, info.ClassType);
            Assert.Equal(expectedParams.Race, info.Race);
            Assert.Equal(expectedParams.Level, info.Level);

            Assert.Equal(expectedParams.Accuracy, info.Accuracy);
            Assert.Equal(expectedParams.AttackRange, info.AttackRange);
            Assert.Equal(expectedParams.AttackSpeed, info.AttackSpeed);
            Assert.Equal(expectedParams.AttackSpeedMultiplier, info.AttackSpeedMultiplier, 3);
            Assert.Equal(expectedParams.Evasion, info.Evasion);
            Assert.Equal(expectedParams.Critical, info.Critical);
            Assert.Equal(expectedParams.MoveSpeedMultiplier, info.MoveSpeedMultiplier, 3);

            Assert.Equal(expectedParams.InventoryCarringWeight, info.InventoryCarringWeight);
            Assert.Equal(expectedParams.MaxWeight, info.MaxWeight);

            Assert.Equal(expectedParams.BaseHP, info.BaseHP);
            Assert.Equal(expectedParams.BaseMP, info.BaseMP);
            Assert.Equal(expectedParams.Mp, info.Mp);
            Assert.Equal(expectedParams.Hp, info.Hp);
            Assert.Equal(expectedParams.PhysAtk, info.PhysAtk);
            Assert.Equal(expectedParams.PhysDef, info.PhysDef);
            Assert.Equal(expectedParams.BaseMagAtkSpd, info.BaseMagAtkSpd);
            Assert.Equal(expectedParams.BasePhysAtkSpd, info.BasePhysAtkSpd);
            Assert.Equal(expectedParams.MagAtk, info.MagAtk);
            Assert.Equal(expectedParams.MagDef, info.MagDef);

            Assert.Equal(expectedParams.DexStat, info.DexStat);
            Assert.Equal(expectedParams.ConStat, info.ConStat);
            Assert.Equal(expectedParams.MenStat, info.MenStat);
            Assert.Equal(expectedParams.StrStat, info.StrStat);
            Assert.Equal(expectedParams.WitStat, info.WitStat);
            Assert.Equal(expectedParams.IntStat, info.IntStat);

            Assert.Equal(expectedParams.MoveSpeedRun_Flight, info.MoveSpeedRun_Flight);
            Assert.Equal(expectedParams.MoveSpeedRun_Floating, info.MoveSpeedRun_Floating);
            Assert.Equal(expectedParams.MoveSpeedRun_Ground, info.MoveSpeedRun_Ground);
            Assert.Equal(expectedParams.MoveSpeedRun_Underwater, info.MoveSpeedRun_Underwater);
            Assert.Equal(expectedParams.MoveSpeedWalk_Flight, info.MoveSpeedWalk_Flight);
            Assert.Equal(expectedParams.MoveSpeedWalk_Floating, info.MoveSpeedWalk_Floating);
            Assert.Equal(expectedParams.MoveSpeedWalk_Ground, info.MoveSpeedWalk_Ground);
            Assert.Equal(expectedParams.MoveSpeedWalk_Underwater, info.MoveSpeedWalk_Underwater);

            Assert.True(scenario.LogOut());
            player.DisconnectWorld();
        }

Как видно, это так наываемый параметрический тест - один и тот же код проверки будет для 20+ наборов данных (или сколько там 1х проф). В этом конкретном тесте только голые пассивки на 1й профе тестятся, но точно так же написаны тесты для оружия, брони, сэтов и для некоторых активных скиллов.

Сэмплы снимал с оригинального С1 (expectedParams), потом тестировал свой сервер на них.
 
Назад
Сверху Снизу