ksteel Опубликовано 2 часа назад Автор Жалоба Поделиться Опубликовано 2 часа назад (изменено) и т.к. в Angel.ini будет прям очень много информации, соответственно скорее всего сам файл Angel.ini будет разделён на разные версии Angel_eng.ini Angel_rus.ini Angel_fr.ini и так далее. В каждом из инишников будет описание как на родном для локали языке, так и на английском. Где "особые" уникальные фиксы будут описываться подробнее (пример: почему его нет в другой локали, а в этой он есть). И сама структура инишников будет переделана - сначала будут идти общие фиксы, которые необходимы всем локалям, а потом будут идти уже узконаправленные фиксы для определённой локали. Изменено 2 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
ksteel Опубликовано 2 часа назад Автор Жалоба Поделиться Опубликовано 2 часа назад (изменено) 1 час назад, olol сказал: Насколько я помню, для плаванья аргонцев движок игры проверяет не ID расы, а ее название, которое отличается для других языков: EN: Argonian DE: Argonier ES: Argoniano FR: Argonien HU: Argóniai IT: Argoniano PL: Argonian PT: Argonianos RU: Аргонианин А у китайского шрифт Win32!!! Фиксить нужно проверку не на название, а на ID расы, которая одинакова для всех языков. Соответственно это исправление должно быть в MCP. хоть я уже и перенёс фикс по "названию расы в игре", ваша мысль просто бриллиантовая! я переделаю этот фикс, сделаю его изначально "универсальным" для всех языков, которые НЕ английские! тем более у нас есть уже явный пример от Angel Death в файле items_info.cpp. Он использует сравнение по ID типа предмета (0x4f4d4d41 = 'AMMO'), а не по строковому имени! Это именно тот подход, который нужен для аргониан! Изменено 2 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
ksteel Опубликовано 2 часа назад Автор Жалоба Поделиться Опубликовано 2 часа назад (изменено) ещё забавно, но факт что в стиме при скачивании En, Fr или De версий нет констракшн сета 😃 совсем безолаберность upd Construction Set и всякие источники показывают только строковое значение ID для расы = Argonian. Совершенно бесполезно. Надо искать числовое значение внутри движка и вновь погружаюсь в декомпиляторы) upd из строчек: 007af2a4 Unable to find race id "%s" in script %s. "Unable to find race id \"%s\" in script %s." и 004fde03 PUSH s_Unable_to_find_race_id_"%s"_in_s_007af2a4 DATA прихожу к выводу, что Race id в "скриптах" передаётся как строка ("%s") (что грустно), а не число, Construction Set говорит, что id аргониан = Argonian, которую нещадно переводит наша (да и вообще любая) локализация. Надежда на то, что Движок анимаций использует числа, а не строку, продолжаю поиски. Можно конечно задать параметр, чтобы пеередавалось не как строка.....но хз чем это будет грозить в рамках всего движка (тотальным ахтунгом, потому что все функции ждут строку, а не число int) upd (далее пойдёт немного хаотичное повествование, т.к. автор поста выпил вина 😄 ) что было выявлено посредством шаманства: 0x004C2B40 - 0x004C3090 - это ОДНА ФУНКЦИЯ выбора анимации MOV EAX - загрузка пути анимации в регистр PUSH EAX - передача пути в функцию загрузки base_animKnA.nif vs argonian_swimKnA.nif - два варианта анимации! Вывод в функции происходит ветвление: Если аргонианин → argonian_swimKnA.nif Если другая раса → base_animKnA.nif А ещё я нашёл ЧИСЛОВУЮ (ура) проверку: dec eax ; jz short loc_4C2B47 ; (Если EAX == 2 → зелёная ветка (аргониане)) Вывод EAX содержит числовой race_id. Теперь смотрим на код в декомпиляторе: Спойлер int __thiscall sub_4C27C0(_DWORD *this) { int j; // esi const char *v3; // eax int v4; // ebx const char *v5; // eax char *v6; // eax char *v7; // eax int v8; // eax int k; // eax char *v10; // eax int v11; // eax int v12; // ecx int v13; // edi bool v14; // zf char *v15; // eax int v16; // edi char **v17; // ebx char *v18; // eax char *v19; // eax int v20; // ebx const char *v21; // eax int *v22; // edi const char *v23; // eax char *v24; // eax char *v25; // ecx char *v26; // eax int v27; // eax int n; // eax char *v29; // eax int v30; // eax int v31; // ecx int v32; // esi int v33; // ebx char *v34; // eax char **v35; // edi char *v36; // esi _DWORD *v37; // ebx int result; // eax int i; // [esp+10h] [ebp-14h] int *v40; // [esp+14h] [ebp-10h] _BYTE *v41; // [esp+14h] [ebp-10h] int m; // [esp+14h] [ebp-10h] char *String1; // [esp+18h] [ebp-Ch] int v44; // [esp+1Ch] [ebp-8h] int *v45; // [esp+20h] [ebp-4h] v44 = sub_4D51B0(&unk_7CB478); String1 = (char *)sub_4D51E0(&unk_7CB478); for ( i = 0; i < 2; ++i ) { for ( j = 0; j < 2; ++j ) { v45 = this + 2 * i + j + 10699; if ( *v45 ) { v3 = (const char *)sub_4C2720(i, j); nullsub_12("Clearing 3D for baseanim '%s'.", v3); sub_4EE5B0(*(this + 2 * i + j + 10695)); sub_471B00(0); } v4 = j + 2 * i; memset(this + 150 * v4 + 10095, 0, 0x258u); v40 = this + v4 + 10695; if ( *v40 ) { v5 = (const char *)sub_4C2720(i, j); nullsub_12("Clearing 3D for baseanim '%s'.", v5); sub_4EE5B0(*v40); sub_471B00(0); } if ( i ) { if ( i == 1 ) { if ( j == 1 ) v6 = aBaseAnimFemale; else v6 = aBaseAnimFemale_0; } else { v6 = 0; } } else { v6 = aBaseAnim1stNif; if ( j != 1 ) v6 = aBaseAnimNif; } if ( (unsigned __int8)sub_47D640(v6) ) { sprintf(v44, aSBaseAnim, off_792674[i]); if ( i ) { if ( i == 1 ) { if ( j == 1 ) v7 = aBaseAnimFemale; else v7 = aBaseAnimFemale_0; } else { v7 = 0; } } else { v7 = aBaseAnim1stNif; if ( j != 1 ) v7 = aBaseAnimNif; } v8 = sub_4EE200(v7, v44); *v45 = v8; if ( v8 ) { for ( k = *(_DWORD *)(v8 + 16); k; k = *(_DWORD *)(k + 32) ) *(this + 150 * v4 + *(unsigned __int8 *)(k + 16) + 10095) = k; } if ( i ) { if ( i == 1 ) { if ( j == 1 ) v10 = aBaseAnimFemale; else v10 = aBaseAnimFemale_0; } else { v10 = 0; } } else { v10 = aBaseAnim1stNif; if ( j != 1 ) v10 = aBaseAnimNif; } sub_47D870(v10, (int)&off_789D88, (int)String1, 0); v11 = sub_4EE0A0(String1); v12 = *v40; v13 = v11; if ( *v40 != v11 ) { if ( v12 ) { v14 = (*(_DWORD *)(v12 + 4))-- == 1; if ( v14 ) (**(void (__thiscall ***)(int, int))v12)(v12, 1); } *v40 = v13; if ( v13 ) ++*(_DWORD *)(v13 + 4); } if ( *v40 ) { if ( i ) { if ( i == 1 ) { if ( j == 1 ) v15 = aBaseAnimFemale; else v15 = aBaseAnimFemale_0; } else { v15 = 0; } } else { v15 = aBaseAnim1stNif; if ( j != 1 ) v15 = aBaseAnimNif; } sub_6EA1A0(v15); sub_472B70(*v40, 1); sub_6EB000(0, 0, 1); } } if ( !i ) { v16 = 0; v17 = &off_78A970; v41 = (char *)&unk_7A6008 + j; do { if ( *v41 != 1 || *(this + 150 * j + v16 + 10095) ) { if ( *v41 == 0xFF && *(this + 150 * j + v16 + 10095) ) { v19 = aBaseAnim1stNif; if ( j != 1 ) v19 = aBaseAnimNif; sub_477400("Please remove anim group (%s) in \"%s\".\r\n", *v17, v19); } } else { v18 = aBaseAnim1stNif; if ( j != 1 ) v18 = aBaseAnimNif; sub_477400("Missing required anim group (%s) in \"%s\".\r\n", *v17, v18); } ++v17; ++v16; v41 += 2; } while ( (int)v17 < (int)off_78ABC8 ); } } } v20 = 0; for ( m = 0; ; v20 = m ) { if ( *(this + v20 + 11156) ) { if ( v20 ) { if ( v20 == 1 ) { v21 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v21 = aArgonianSwimkn; } else { v21 = 0; } } else { v21 = aBaseAnimknaNif; } nullsub_12("Clearing 3D for baseanim '%s'.", v21); sub_4EE5B0(*(this + v20 + 11153)); sub_471B00(0); } memset(this + 150 * v20 + 10703, 0, 0x258u); v22 = this + v20 + 11153; if ( *v22 ) { if ( v20 ) { if ( v20 == 1 ) { v23 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v23 = aArgonianSwimkn; } else { v23 = 0; } } else { v23 = aBaseAnimknaNif; } nullsub_12("Clearing 3D for baseanim '%s'.", v23); sub_4EE5B0(*v22); sub_471B00(0); } if ( v20 ) { if ( v20 == 1 ) { v24 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v24 = aArgonianSwimkn; } else { v24 = 0; } } else { v24 = aBaseAnimknaNif; } if ( (unsigned __int8)sub_47D640(v24) ) { if ( v20 ) { if ( v20 == 1 ) { v25 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v25 = aArgonianSwimkn; } else { v25 = 0; } } else { v25 = aBaseAnimknaNif; } if ( v20 ) { if ( v20 == 1 ) { v26 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v26 = aArgonianSwimkn; } else { v26 = 0; } } else { v26 = aBaseAnimknaNif; } v27 = sub_4EE200(v26, (int)v25); *(this + v20 + 11156) = v27; if ( v27 ) { for ( n = *(_DWORD *)(v27 + 16); n; n = *(_DWORD *)(n + 32) ) *(this + 150 * v20 + *(unsigned __int8 *)(n + 16) + 10703) = n; } if ( v20 ) { if ( v20 == 1 ) { v29 = aBaseAnimkna1st; } else if ( v20 == 2 ) { v29 = aArgonianSwimkn; } else { v29 = 0; } } else { v29 = aBaseAnimknaNif; } sub_47D870(v29, (int)&off_789D88, (int)String1, 0); v30 = sub_4EE0A0(String1); v31 = *v22; v32 = v30; if ( *v22 != v30 ) { if ( v31 ) { v14 = (*(_DWORD *)(v31 + 4))-- == 1; if ( v14 ) (**(void (__thiscall ***)(int, int))v31)(v31, 1); } *v22 = v32; if ( v32 ) ++*(_DWORD *)(v32 + 4); } if ( *v22 ) { if ( v20 ) { v33 = v20 - 1; if ( v33 ) { if ( v33 == 1 ) v34 = aArgonianSwimkn; else v34 = 0; } else { v34 = aBaseAnimkna1st; } } else { v34 = aBaseAnimknaNif; } sub_6EA1A0(v34); sub_472B70(*v22, 1); sub_6EB000(0, 0, 1); } } v35 = &off_78A970; v36 = (char *)&unk_7A6008; v37 = this + 10703; do { if ( *v36 != 1 || *v37 ) { if ( *v36 == -1 ) { if ( *v37 ) sub_477400("Please remove anim group (%s) in \"%s\".\r\n", *v35, aBaseAnimknaNif); } } else { sub_477400("Missing required anim group (%s) in \"%s\".\r\n", *v35, aBaseAnimknaNif); } v36 += 2; ++v37; ++v35; } while ( (int)v36 < (int)aHandlerCollisi ); result = ++m; if ( m >= 3 ) break; } return result; } из которого делаю выводы: if ( v20 ) { if ( v20 == 1 ) { v24 = aBaseAnimkna1st;} else if ( v20 == 2 ){ v24 = aArgonianSwimkn;} else { v24 = 0; } } v24 = aBaseAnimkna1st; // "base_animkna1st.nif" v24 = aArgonianSwimkn; // "argonian_swimKnA.nif" - аргониане получается переменная v20 v20 = 0 → base_animkna.nif (стандартная анимация) v20 = 1 → base_animkna1st.nif (анимация от первого лица) v20 = 2 → argonian_swimKnA.nif (специальная анимация аргониан) НО где же определяется эта переменная? Смотрим выше: v20 = 0; for ( m = 0; ; v20 = m ) {... огромный блок кода ... result = ++m; if ( m >= 3 ) break; } Вывод v20 (m) проходит значения 0, 1, 2, v20 = 2 соответствует аргонианам, Значит race_id аргониан = 2. Нам надо перехватить в момент проверки когда v20 = 2 и обеспечить загрузку правильной анимации. Изменено 1 час назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения
Для публикации сообщений создайте учётную запись или авторизуйтесь
Вы должны быть пользователем, чтобы оставить комментарий
Создать учетную запись
Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!
Регистрация нового пользователяВойти
Уже есть аккаунт? Войти в систему.
Войти