You are not logged in.
Смотри в сторону Vehicles Special Abilities.
Так лучше не делать - может возникнуть проблема с Limit Adjuster, где выделяется новый массив указателей на model info.
Есть функция, которая возвращает model info по ид модели.
0x403DA0 ; CBaseModelInfo *__cdecl CModelInfo::GetModelInfo(int modelId)
Записываешь в ячейку массива число.
Сдвигаешь на (7 - i) * 4 бит вправо (i - номер элемента в массиве, начиная с 0) (Rsh).
Убираешь всё, кроме нижних 4 бит (And).
Должны, но не выгружаются.
{$CLEO .cs}
0000:
while 8AB0: is_key_pressed 9
wait 0
end
while true
0390: load_texture_dictionary 'LD_CARD'
038F: load_sprite 1 from "cd1s"
03F0: use_text_commands 1
038D: draw_sprite 1 position 590.0 390.0 size 50.0 50.0 colour 255 255 255 255
wait 0
0@ += 1
03F0: use_text_commands 0
0AA5: call_function 0x82D4C0 num_params 3 pop 3 8 0 1@v
0AA5: call_function 0x801E90 num_params 3 pop 3 1@v 0x804B60 0xC97B28
0AA5: call_function 0x801E90 num_params 3 pop 3 2@v 0x804B60 0xC97B54
0A8D: read_memory 0xC8800C size 4 vp 0 store_to 5@
0A8E: 3@ = 5@ + 0x8
0A8D: read_memory 3@ size 4 vp 0 store_to 3@
0AA8: call_method_return 0x54F6B0 struct 5@ num_params 0 pop 0 store_to 4@
0AD1: print_formatted_now "Times Loaded: %u~n~Engine Txds: %u Engine Textures: %u~n~Txd Pool capacity: %u Txd Pool size: %u" time 150 0@ 1@ 2@ 3@ 4@
0391: remove_texture_dictionary
end
Попробуй написать скрипт, в котором идёт загрузка и выгрузка txd. Проверь, повторится ли там эта проблема.
Нету такого "адреса" и опкода.
Можно создать счётчик, который будет хранить кол-во "стартов" игры. Если этот счётчик больше 1 - значит, был произведён перезапуск.
Т.е. надо будет самому создать такой опкод - например, с CLEO SDK.
Не думаю, что есть такие "адреса".
Можно создать счётчик, который будет хранить кол-во "стартов" игры. Если этот счётчик больше 1 - значит, был произведён перезапуск.
Некоторые структуры
https://drive.google.com/uc?export=down … FgtMHhHcTA
Что делать:
В IDA:
File -> Load file -> Parse C header file -> Выбрать gta3.h
Перейти на вкладку Local Types
View -> Open subviews -> Local types
В этой вкладке:
Выделить всё содержимое (Ctrl+A) -> ПКМ -> Synchronize to idb
Используй базу на сайте GTAG для поиска опкодов.
http://gtag.gtagaming.com/opcode-database/opcode/05C5/
Или установи последнюю версию SB c этими опкодами.
Хотя, я не уверен, что после такого получится нормально компилировать уже готовые исходники скриптов (может поменяться порядок параметров в опкоде)
Молодец.
Сколько времени ушло на это? 
DK22Pac wrote:Вот небольшая програмка, которая заменяет все "проблемные" float-числа на целые.
http://i.imgur.com/FhVHspIm.pngИспользование: скопировать код своего скрипта, запустить на выполнение ("Run").
Круто, но к сожалению, работает только если число отделено пробелами. Запись
0@ = Object.Create(#lc_sky04, -3500.0, 0.0, -50.0)не "видит", пока число не отделено так:
0@ = Object.Create(#lc_sky04, -3500.0 , 0.0, -50.0)
Спасибо, поправил.
http://cpp.sh/4bpp
PS Эта проблема не относится к Vice City, там float 4-байтный.
Вариант #1 (самый простой и самый быстрый) - написать свой опкод, используя CLEO SDK и plugin-sdk.
Самое главное преимущество такого подхода, относительно скрипта - то, что скрипт остается "чистым" и понятным.
{$CLEO}
{$OPCODE 1A04=2,%2d% = get_player %1d% breath_percentage}
0000:
while true
wait 0
if
0256: is_player_playing 0
then
1A04: 0@ = get_player 0 breath_percentage
0AD1: print_formatted_now "%.2f" time 100 0@
end
endКод опкода
// 1A04: 0@ = get_player 0 breath_percentage
static OpcodeResult WINAPI Opcode_GetPlayerBreathPercentage(CScriptThread *thread) {
int playerId = CLEO_GetIntOpcodeParam(thread);
if ((playerId == 0 || playerId == 1) && CWorld::Players[playerId].m_pPed)
CLEO_SetFloatOpcodeParam(thread, CWorld::Players[playerId].m_pPed->m_pPlayerData->m_fBreath / CallAndReturn<float, 0x559AF0>(8));
else
CLEO_SetFloatOpcodeParam(thread, 0.0f);
return OR_CONTINUE;
}Вариант #2 (для извращенцев)
{$CLEO}
0AC6: get_label_pointer @pop_float store_to 31@
while true
wait 0
if
0256: is_player_playing 0
then
01F5: get_player_char 0 store_to 0@
0A96: get_char_pointer 0@ store_to 1@
1@ += 0x480
0A8D: read_memory 1@ size 4 vp 0 store_to 1@
1@ += 0x44
0A8D: read_memory 1@ size 4 vp 0 store_to 1@
0AA5: call_function 0x559AF0 num_params 1 pop 1 params 8
0AC7: get_var_pointer 2@ store_to 3@
0AA5: call_function 31@ num_params 1 pop 1 params 3@
0073: 1@ /= 2@
0AD1: print_formatted_now "%.2f" time 100 1@
end
end
:pop_float
hex
8B 44 24 04 // mov eax, [esp + 4]
D9 18 // fstp [eax]
C3 // retn
endВариант #3 (для психопатов)
UP: Передумал писать... 
все float(дробные числа) компилируются в короткий float(2 байта), а то есть значения не точные и нельзя выставить значение выше 2048.0
Это лимит скриптового движка игры, а не SannyBuilder'а.
после декомпиляции переводит в Dword(4 байта целые числа). А если дело касается минусовых, то вообще не вообще не восстановить(ctrl+H).
Вопрос: как все это исправить?
Разобраться, что такое исходный файл и не декомпилировать свои скрипты.
как сделать так чтобы 3000.0 компилировался в нормальный float(453B8000)
Внести изменения в скриптовый движок игры. Добавить новый тип параметра - 4-байтный float, его чтение и запись.
Написать программу, которая бы в скомпилировааном скрипте заменяла все 2-байтные float на этот новый тип.
Jack_Savage, самое простое решение - переустановить студию, при установке выбирать Windows SDK. Ну и поддержку WinXP не забыть.
https://github.com/DK22Pac/plugin-sdk/w … -Community
В plugin-sdk есть пример по спавну педа.
https://github.com/DK22Pac/plugin-sdk/t … PedSpawner
Можно вызвать функцию CPopulation::AddPed или самому создать обьект класса CCivilianPed/CCopPed/CEmergencyPed и настроить ему позицию (CPlaceable::SetPosn) и добавить в игровой мир (CWorld::Add)
plugin.lib получается после сборки проекта plugin_sa в решении plugin-sdk.

Нет. Опкоды я визываю через SCM hook в C++
Что значит "аля asi"? Это динамическая библиотека? Чем она подгружается, если не asi-loader'ом?
Кламп это графический обьект RenderWare.
"Взять" его можно в plugin-sdk, подключив хедер RenderWare.h.
Можешь выложить проект со своим плагином, я тебе помогу подключить туда библиотеку и хедеры plugin-sdk.
Zin_869731, как варианты - нету нужных прав (администраторских) для внесения изменений, какие-то проблемы с интернет-соединением (часть пакетов докачивается уже при установке), не хватает места на диске.
Ещё тут можно посмотреть
https://support.microsoft.com/ru-ru/hel … 2015-setup
VS2017 я сам ещё не пробовал. Может попробуешь VS2015?
Хук ставится в место, где название анимации "переводится" в хеш. Делаем это для того, чтобы иметь возможность "перевести" хеш обратно в название (сохраняем название в карту с ключом-хешем).
static CdeclEvent<AddressList<0x4CF2D8, H_CALL>, PRIORITY_AFTER, ArgPickN<char *, 0>, unsigned int(char *)> myStoreAnimNameEvent; // Хук
myStoreAnimNameEvent += [](char *name) {
animNamesMap[CKeyGen::GetUppercaseKey(name)] = name; // Сохраняем в карту
};Получаем кол-во активных анимаций педа. Одновременно пед может проигрывать несколько анимаций (например - стоит и прицеливается)
unsigned int numAnimations = RpAnimBlendClumpGetNumAssociations(ped->m_pRwClump);
if (numAnimations > 0) { // Если есть анимацииПолучаем описание первой анимации
CAnimBlendAssociation *association = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump);
Переводим хеш в название, используя нашу карту
sprintf(text, "%d. %s", counter + 1, animNamesMap[association->m_pAnimBlendHierarchy->m_hashKey].c_str());
В цикле получаем следующую анимацию, таким образом, "проходимся" по всем анимациям.
association = RpAnimBlendGetNextAssociation(association);
Ну и, если пишешь код C++ - надо искать хотя бы уроки в интернете и разбираться.
http://cleo.li/ru/plugins.html
CLEO 4 позволяет создавать новые опкоды, используя исходные коды CLEO SDK. В стартовом комплекте библиотеки можно найти три примера таких плагинов:
IniFiles.cleo - работа с INI файлами;
FileSystemOperations.cleo - работа с файлами и папками.
IntOperations.cleo - битовые операции над целыми числами (AND, OR, XOR, и т.д.).
#include "plugin.h"
#include "CLEO.h"
#include "game_sa\CPools.h"
#include "game_sa\common.h"
#include "game_sa\CKeyGen.h"
#include <map>
using namespace plugin;
/*
SASCM.ini
1A01=2,%2d% = get_char %1d% num_of_active_animations
1A02=2,%2d% = get_char %1d% active_animations
1A03=2,%2d% = get_char %1d% main_active_animation
opcodes.txt
1A01: 0@ = get_char 1@ num_of_active_animations
1A02: 0@v = get_char 1@ active_animations
1A03: 0@v = get_char 1@ main_active_animation
*/
class GetPedAnimationOpcodes {
public:
static char animNameGlobalName[1024];
static std::map<unsigned int, std::string> animNamesMap;
static CdeclEvent<AddressList<0x4CF2D8, H_CALL>, PRIORITY_AFTER, ArgPickN<char *, 0>, unsigned int(char *)> myStoreAnimNameEvent;
static void MyStoreAnimName(char *name) {
animNamesMap[CKeyGen::GetUppercaseKey(name)] = name;
}
// 1A01: 0@ = get_char 1@ num_of_active_animations
static OpcodeResult WINAPI Opcode_GetPedNumberOfActiveAnimations(CScriptThread *thread) {
CPed *ped = CPools::GetPed(CLEO_GetIntOpcodeParam(thread));
if (ped && ped->m_pRwClump)
CLEO_SetIntOpcodeParam(thread, RpAnimBlendClumpGetNumAssociations(ped->m_pRwClump));
else
CLEO_SetIntOpcodeParam(thread, 0);
return OR_CONTINUE;
}
// 1A02: 0@v = get_char 1@ active_animations
static OpcodeResult WINAPI Opcode_GetPedAnimationsNames(CScriptThread *thread) {
CPed *ped = CPools::GetPed(CLEO_GetIntOpcodeParam(thread));
animNameGlobalName[0] = '\0';
if (ped && ped->m_pRwClump) {
CAnimBlendAssociation *association = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump);
if(association){
strcpy_s(animNameGlobalName, animNamesMap[association->m_pAnimBlendHierarchy->m_hashKey].c_str());
for (association = RpAnimBlendGetNextAssociation(association); association; association = RpAnimBlendGetNextAssociation(association)) {
strcat_s(animNameGlobalName, ",");
strcat_s(animNameGlobalName, animNamesMap[association->m_pAnimBlendHierarchy->m_hashKey].c_str());
}
}
}
int paramType = CLEO_GetOperandType(thread);
if (paramType == localVarVString || paramType == localVarSString)
CLEO_WriteStringOpcodeParam(thread, animNameGlobalName);
else
CLEO_SetIntOpcodeParam(thread, reinterpret_cast<int>(animNameGlobalName));
return OR_CONTINUE;
}
// 1A03: 0@v = get_char 1@ main_active_animation
static OpcodeResult WINAPI Opcode_GetPedMainAnimationName(CScriptThread *thread) {
CPed *ped = CPools::GetPed(CLEO_GetIntOpcodeParam(thread));
animNameGlobalName[0] = '\0';
if (ped && ped->m_pRwClump) {
CAnimBlendAssociation *association = RpAnimBlendClumpGetMainAssociation(ped->m_pRwClump, NULL, NULL);
if(association)
strcpy_s(animNameGlobalName, animNamesMap[association->m_pAnimBlendHierarchy->m_hashKey].c_str());
}
int paramType = CLEO_GetOperandType(thread);
if (paramType == localVarVString || paramType == localVarSString)
CLEO_WriteStringOpcodeParam(thread, animNameGlobalName);
else
CLEO_SetIntOpcodeParam(thread, reinterpret_cast<int>(animNameGlobalName));
return OR_CONTINUE;
}
GetPedAnimationOpcodes() {
CLEO_RegisterOpcode(0x1A01, Opcode_GetPedNumberOfActiveAnimations);
CLEO_RegisterOpcode(0x1A02, Opcode_GetPedAnimationsNames);
CLEO_RegisterOpcode(0x1A03, Opcode_GetPedMainAnimationName);
myStoreAnimNameEvent.Add(MyStoreAnimName);
}
} pedAnimOpcodes;
char GetPedAnimationOpcodes::animNameGlobalName[1024];
std::map<unsigned int, std::string> GetPedAnimationOpcodes::animNamesMap;
CdeclEvent<AddressList<0x4CF2D8, H_CALL>, PRIORITY_AFTER, ArgPickN<char *, 0>, unsigned int(char *)> GetPedAnimationOpcodes::myStoreAnimNameEvent;{$CLEO}
{$OPCODE 1A01=2,%2d% = get_char %1d% num_of_active_animations}
{$OPCODE 1A02=2,%2d% = get_char %1d% active_animations}
{$OPCODE 1A03=2,%2d% = get_char %1d% main_active_animation}
thread 'ANIMOPC'
while true
wait 0
if
0256: is_player_playing 0
then
01F5: get_player_char 0 store_to 0@
1A02: 1@ = get_char 0@ active_animations
0ACC: print_string 1@ time 100
end
end
Можно сделать новый опкод в .cleo-плагине.
В конструкцию к while можно подставить только одно условие. Но можно сделать так:
while true
wait 0 // не забываем добавить задержку в цикл с (вероятно) большим кол-вом повторений
// обновляем 2@
if or
2@ < 50
2@ > 78
then
break
end
end
Cпасибо, я не хочу юзать plugin sdk просто я не понимаю что там и как вообще...
Ну тогда не знаю, как тебе ещё помочь
Такой код
*(int*)0xB6F5F0 + 0x15C;
я писать не буду.
Могу опкодами сделать, разве что.
int numAnimations = RpAnimBlendClumpGetNumAssociations(ped->m_pRwClump);
Эта функция получает кол-во активных анимаций, прицепленных к клампу.
Вот пример с использованием plugin-sdk - вывод списка анимаций игрока.
Main.cpp
#include "plugin.h"
#include "game_sa\common.h"
#include "game_sa\CFont.h"
#include "game_sa\CSprite2d.h"
#include "game_sa\CKeyGen.h"
#include <map>
using namespace plugin;
class AnimNamesViewer {
public:
AnimNamesViewer() {
static std::map<unsigned int, std::string> animNamesMap;
static CdeclEvent<AddressList<0x4CF2D8, H_CALL>, PRIORITY_AFTER, ArgPickN<char *, 0>, unsigned int(char *)> myStoreAnimNameEvent;
myStoreAnimNameEvent += [](char *name) {
animNamesMap[CKeyGen::GetUppercaseKey(name)] = name;
};
Events::drawingEvent += [] {
CPed *ped = FindPlayerPed();
if (ped && ped->m_pRwClump) {
unsigned int numAnimations = RpAnimBlendClumpGetNumAssociations(ped->m_pRwClump);
if (numAnimations > 0) {
CSprite2d::DrawRect(CRect(SCREEN_COORD_RIGHT(310.0f), SCREEN_COORD(265.0f), SCREEN_COORD_RIGHT(40.0f),
SCREEN_COORD(275.0f + 50.0f * numAnimations)), CRGBA(30, 30, 30, 255));
CFont::SetAlignment(ALIGN_LEFT);
CFont::SetColor(CRGBA(255, 255, 255, 255));
CFont::SetOutlinePosition(0);
CFont::SetDropColor(CRGBA(0, 0, 0, 255));
CFont::SetFontStyle(FONT_MENU);
CFont::SetProp(true);
CFont::SetWrapx(SCREEN_COORD_MAX_X * 2);
CFont::SetBackground(false, false);
int counter = 0;
CAnimBlendAssociation *association = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump);
while (association) {
static char text[256];
sprintf(text, "%d. %s", counter + 1, animNamesMap[association->m_pAnimBlendHierarchy->m_hashKey].c_str());
if (association->m_wFlags & 0x10)
strcat(text, " (P)");
CFont::SetScale(SCREEN_MULTIPLIER(0.6f), SCREEN_MULTIPLIER(1.2f));
float width = CFont::GetStringWidth(text, true, false);
if(width > SCREEN_COORD(250.0f))
CFont::SetScale(SCREEN_MULTIPLIER(0.6f * SCREEN_COORD(250.0f) / width), SCREEN_MULTIPLIER(1.2f));
float baseY = SCREEN_COORD(300.0f) + SCREEN_COORD(50.0f) * counter;
CFont::PrintString(SCREEN_COORD_RIGHT(300.0f), baseY - SCREEN_COORD(25.0f), text);
CSprite2d::DrawRect(CRect(SCREEN_COORD_RIGHT(300.0f), baseY, SCREEN_COORD_RIGHT(50.0f), baseY + SCREEN_COORD(8.0f)),
CRGBA(130, 130, 130, 255));
CSprite2d::DrawRect(CRect(SCREEN_COORD_RIGHT(300.0f), baseY,
SCREEN_COORD_RIGHT(300.0f - association->m_fCurrentTime / association->m_pAnimBlendHierarchy->m_fTotalTime * 250.0f),
baseY + SCREEN_COORD(6.0f)), CRGBA(210, 210, 210, 255));
association = RpAnimBlendGetNextAssociation(association);
++counter;
}
CFont::DrawFonts();
}
}
};
}
} animNamesView;При желании можно оформить в виде опкода.