Pesquisa resultou em 5 ocorrências

por Junior_Djjr
26 Abr 2019, 20:32
Fórum: Tutoriais
Tópico: Como o Tuning Mod v2 funciona?
Respostas: 6
Exibições: 1583
Gênero:

Re: Como o Tuning Mod v2 funciona?

Fiz um vídeo mostrando todo o código do Tuning Mod v3.

Demorou 21 minutos para passar o código rapidamente.
por Junior_Djjr
14 Jul 2018, 18:32
Fórum: Tutoriais
Tópico: Como o Tuning Mod v2 funciona?
Respostas: 6
Exibições: 1583
Gênero:

Como o Tuning Mod funciona?

Um pouco sobre a leitura dos arquivo .tmv
Imagem
Logo no início do arquivo usei 4 bytes sendo que o número da versão é simplesmente 1, 2, 3... Eu poderia usar por exemplo 0x02010100 (v2.1.1.0) para marcar a versão completa mas era desnecessário. Enfim, com o número da versão eu tenho um controle sobre como agir na leitura daquele arquivo.
Num arquivo de constantes tem a versão do mod. A atual sendo feita (v2.2) é a versão 7: CurrentVersion = 7.
Antes da leitura eu checo se a versão do arquivo é compatível com a atual leitura do mod.
Imagem
Ou seja, se o carro foi salvo na versão "3" (que é provavelmente o Tuning Mod v1.5) não será lido, aparecerá uma mensagem na tela.
Por eu não ter usado header é necessário ler a quantia exata de bytes de cada dado, e basta 1 único byte para todo o resto do arquivo não ser mais lido. Bem chato, mas não tô com saco para implementar headers e tal e o salvamento e carregamento está muito estável pelo visto.
Por exemplo a partir da versão 5 há armazenamento do nickname e data no início do arquivo em vez de no fim, portanto...:
Imagem
Simplesmente isto.
Eu só recomendo que faça isso caso o seu arquivo seja realmente muito simples, se você for fazer um mod que faça uma leitura complexa como o Tuning Mod e é propenso à atualizações, não faça como eu.
Procure soluções melhores, que cada parte pode estar em diferentes partes do arquivo, e o arquivo tenha algum header ou algo assim para informar onde está cada parte, por exemplo onde em qual offset do arquivo eu teria que ler para conseguir as informações das peças tuning.

Uma dica é você estudar a estrutura de um arquivo .bmp, é um formato de arquivo muito simples de ser lido, eu fiz um leitor de .bmp num curso online da Havard University, inclusive um editor hardcoded (eu alterava o tamanho da imagem e outras propriedades dos pixeis por código, por exemplo clareá-los, trocar a cor etc), foi muito divertido, é uma boa introdução para você aprender sobre leitura de arquivos e criar o seu próprio formato de arquivo.
por Junior_Djjr
30 Jun 2018, 19:05
Fórum: Tutoriais
Tópico: Como o Tuning Mod v2 funciona?
Respostas: 6
Exibições: 1583
Gênero:

Como o Tuning Mod funciona?

Acho que terminei.
por Junior_Djjr
30 Jun 2018, 13:52
Fórum: Tutoriais
Tópico: Como o Tuning Mod v2 funciona?
Respostas: 6
Exibições: 1583
Gênero:

Como o Tuning Mod funciona?

Peças

Certo vamos finalmente falar sobre a parte principal do mod: a instalação de novas peças no modelo do carro.

Como na BMS Sound, inicialmente o Tuning Mod teria a instalação de peças como objetos do mapa grudados no carro, no entanto na GTA Forums DK22Pac me disse que eu na verdade devia "criar uma cópia do modelo como um atomic e adicionar no clump do veículo". Eu criei um tópico perguntando como fazendo a mínima ideia do que diabos é isto, e o Wesser me deu um código que salvou a minha vida. Foi o kickstart para o Tuning Mod ficar bom.

Código: Selecionar tudo

{$CLEO}

0000: NOP

const
    CARCOMPS_EXTRA1 = 2
    CARCOMPS_EXTRA2 = 4
end
const
    PAD1           = 0
end
const
    LEFTSHOULDER1  = 4
end
const
    MODELTYPE_BOAT = 5
    MODELTYPE_BIKE = 9
    MODELTYPE_BMX  = 10
end
const
    _HOOK_004C96F5_CModelCars__cloneData = [email protected]
    _HOOK_004C9763_CModelCars__cloneData = [email protected]
    _getModelPtr                         = 0x00403DA0
    _rwFrameClone                        = [email protected]
    _rwFrameAddChild                     = [email protected]
    _rwFrameDestroy                      = [email protected]
    _rwFrameFindNodeByName               = 0x004C5400
    _rwFrameGetNodeName                  = 0x0072FB30
    _rpClumpAddAtomic                    = [email protected]
    _rpClumpRemoveAtomic                 = [email protected]
    _rpClumpFindFrameById                = 0x004C53C0
    _rpAtomicClone                       = [email protected]
    _rpAtomicSetFrame                    = [email protected]
    _rpAtomicDestroy                     = [email protected]
    _rpAtomicSetCarMatFX                 = 0x004C9410
end

0A9F: [email protected] = current_thread_pointer
0A8E: [email protected] = [email protected] + 0x10 // CScriptThread.m_iBaseAddress
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0

0A8F: _HOOK_004C96F5_CModelCars__cloneData = [email protected] - @HOOK_004C96F5_CModelCars__cloneData
0A8F: _HOOK_004C9763_CModelCars__cloneData = [email protected] - @HOOK_004C9763_CModelCars__cloneData
0A8E: [email protected] = 0x00537338 + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rwFrameClone = [email protected] + [email protected]
0A8E: [email protected] = 0x004C9269 + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rwFrameAddChild = [email protected] + [email protected]
0A8E: [email protected] = 0x004C4473 + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rwFrameDestroy = [email protected] + [email protected]
0A8E: [email protected] = 0x004C6CC1 + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rpClumpAddAtomic = [email protected] + [email protected]
0A8E: [email protected] = 0x004C6CBA + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rpClumpRemoveAtomic = [email protected] + [email protected]
0A8E: [email protected] = 0x004C44EE + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rpAtomicClone = [email protected] + [email protected]
0A8E: [email protected] = 0x004C450A + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rpAtomicSetFrame = [email protected] + [email protected]
0A8E: [email protected] = 0x004C446D + 0x01
0A8E: [email protected] = [email protected] + 0x04
0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 1
0A8E: _rpAtomicDestroy = [email protected] + [email protected]

// Make CModelCars__cloneData cloning the primary extra frame to copy also the node name.
0A8E: [email protected] = 0x004C96F5 + 0x05
0A8F: [email protected] = _HOOK_004C96F5_CModelCars__cloneData - [email protected]
0A8C: write_memory 0x004C96F5 size 1 value 0xE9 virtual_protect 1
0A8C: write_memory 0x004C96F6 size 4 value [email protected] virtual_protect 1
0A8E: [email protected] = _HOOK_004C96F5_CModelCars__cloneData + 0x08
0A8E: [email protected] = [email protected] + 0x04
0A8F: [email protected] = _rwFrameClone - [email protected]
0A8C: write_memory [email protected] size 4 value [email protected] virtual_protect 1
0A8E: [email protected] = _HOOK_004C96F5_CModelCars__cloneData + 0x0F
0A8E: [email protected] = [email protected] + 0x04
0A8F: [email protected] = 0x004C9715 - [email protected]
0A8C: write_memory [email protected] size 4 value [email protected] virtual_protect 1
0A8C: write_memory 0x004C971F size 1 value 0x24 virtual_protect 1
0A8C: write_memory 0x004C972A size 1 value 0x28 virtual_protect 1
0A8C: write_memory 0x004C9735 size 1 value 0x34 virtual_protect 1
0A8C: write_memory 0x004C9738 size 1 value 0x20 virtual_protect 1
// Do the same thing for the secondary extra frame.
0A8E: [email protected] = 0x004C9763 + 0x05
0A8F: [email protected] = _HOOK_004C9763_CModelCars__cloneData - [email protected]
0A8C: write_memory 0x004C9763 size 1 value 0xE9 virtual_protect 1
0A8C: write_memory 0x004C9764 size 4 value [email protected] virtual_protect 1
0A8E: [email protected] = _HOOK_004C9763_CModelCars__cloneData + 0x08
0A8E: [email protected] = [email protected] + 0x04
0A8F: [email protected] = _rwFrameClone - [email protected]
0A8C: write_memory [email protected] size 4 value [email protected] virtual_protect 1
0A8E: [email protected] = _HOOK_004C9763_CModelCars__cloneData + 0x0F
0A8E: [email protected] = [email protected] + 0x04
0A8F: [email protected] = 0x004C9783 - [email protected]
0A8C: write_memory [email protected] size 4 value [email protected] virtual_protect 1
0A8C: write_memory 0x004C978D size 1 value 0x24 virtual_protect 1
0A8C: write_memory 0x004C9798 size 1 value 0x28 virtual_protect 1
0A8C: write_memory 0x004C97A3 size 1 value 0x34 virtual_protect 1
0A8C: write_memory 0x004C97A6 size 1 value 0x20 virtual_protect 1

while true
    if
    00DD:  actor $PLAYER_ACTOR driving_car_with_model #BF400
    then
        03C0: [email protected] = actor $PLAYER_ACTOR car
        0441: [email protected] = car [email protected] model
        if
        00E1:  player PAD1 pressed_key LEFTSHOULDER1 // Secondary Fire
        then
            0AA7: call_function _getModelPtr num_params 1 pop 1 iId [email protected] pclInfo [email protected]
            [email protected] += 0x5C // CModelCars.m_paclVehicleStruct
            0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
            0A8E: [email protected] = [email protected] + 0x030C // CVehicleStruct.m_iNumExtras
            0A8D: [email protected] = read_memory [email protected] size 1 virtual_protect 0
            if
                [email protected] > 0
            then
                0A8E: [email protected] = [email protected] + 0x3C // CModelCars.m_eType
                0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                0A97: [email protected] = car [email protected] struct
                0A8E: [email protected] = [email protected] + 0x0018 // CVehicle.m_clEntity.m_pstRpClump
                0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                if and
                    [email protected] >= MODELTYPE_BOAT
                    [email protected] <> MODELTYPE_BIKE
                    [email protected] <> MODELTYPE_BMX
                then
                    0A8E: [email protected] = [email protected] + 0x04 // SRpClump.m_stObject.m_pstParent
                    0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                else
                    0AA7: call_function _rpClumpFindFrameById num_params 2 pop 2 iHierarchyId 1 pstRpClump [email protected] pstRwFrame [email protected]
                    if
                        [email protected] == 0x00000000
                    then
                        0A8E: [email protected] = [email protected] + 0x04 // SRpClump.m_stObject.m_pstParent
                        0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                    end
                end
                0A8E: [email protected] = [email protected] + 0x02F4 // CVehicleStruct.m_apstExtraAtomics (array_sizeof = 6)
                0A8E: [email protected] = [email protected] + 0x0438 // CVehicle.m_cPrimaryExtraId
                0A8D: [email protected] = read_memory [email protected] size 1 virtual_protect 0
                if
                88B7:  not test [email protected] bit 7
                then
                    0A90: [email protected] = [email protected] * 0x04 // sizeof(SRwFrame *)
                    005A: [email protected] += [email protected]
                    0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                    0A8E: [email protected] = [email protected] + 0x04 // SRpAtomic.m_stObject.m_stObject.m_pstParent
                    0A8D: [email protected] = read_memory 12@ size 4 virtual_protect 0
                    if
                        [email protected] <> 0x00000000
                    then
                        0AA7: call_function _rwFrameGetNodeName num_params 1 pop 1 pstRwFrame [email protected] pszNodeName [email protected]
                        0AA7: call_function _rwFrameFindNodeByName num_params 2 pop 2 pszNodeName [email protected] pstRwFrame [email protected] pstRwFrame [email protected]
                        if
                            [email protected] <> 0x00000000
                        then
                            0A8E: [email protected] = [email protected] + 0x90 // SRwFrame.m_stAtomicList.m_pstNext
                            0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                            [email protected] -= 0x08 // SRpAtomic
                            0AA5: call _rpClumpRemoveAtomic num_params 2 pop 2 pstRpAtomic [email protected] pstRpClump [email protected]
                            0AA5: call _rpAtomicDestroy num_params 1 pop 1 pstRpAtomic [email protected]
                            0AA5: call _rwFrameDestroy num_params 1 pop 1 pstRwFrame [email protected]
                        end
                    end
                end
                0A90: [email protected] = CARCOMPS_EXTRA1 * 0x04 // sizeof(SRwFrame *)
                005A: [email protected] += [email protected]
                0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                if
                    [email protected] <> 0x00000000
                then
                    0A8E: [email protected] = [email protected] + 0x04 // SRpAtomic.m_stObject.m_stObject.m_pstParent
                    0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                    0AA7: call_function _rpAtomicClone num_params 1 pop 1 pstRpAtomic [email protected] pstRpAtomic [email protected]
                    0AA7: call_function _rwFrameClone num_params 1 pop 1 pstRwFrame [email protected] pstRwFrame [email protected]
                    0AA5: call _rpAtomicSetFrame num_params 2 pop 2 pstRwFrame [email protected] pstRpAtomic [email protected]
                    0AA5: call _rpClumpAddAtomic num_params 2 pop 2 pstRpAtomic [email protected] pstRpClump [email protected]
                    0AA5: call _rwFrameAddChild num_params 2 pop 2 pstRwChild [email protected] pstRwFrame [email protected]
                    0AA5: call _rpAtomicSetCarMatFX num_params 2 pop 2 iMaterialId 0 pstRpAtomic [email protected]
                    0A8C: write_memory [email protected] size 1 value CARCOMPS_EXTRA1 virtual_protect 0
                end
                0A8E: [email protected] = [email protected] + 0x0439 // CVehicle.m_cSecondaryExtraId
                0A8D: [email protected] = read_memory [email protected] size 1 virtual_protect 0
                if
                88B7:  not test [email protected] bit 7
                then
                    0A90: [email protected] = [email protected] * 0x04 // sizeof(SRwFrame *)
                    005A: [email protected] += [email protected]
                    0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                    if
                        [email protected] <> 0x00000000
                    then
                        0A8E: [email protected] = [email protected] + 0x04 // SRpAtomic.m_stObject.m_stObject.m_pstParent
                        0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                        0AA7: call_function _rwFrameGetNodeName num_params 1 pop 1 pstRwFrame [email protected] pszNodeName [email protected]
                        0AA7: call_function _rwFrameFindNodeByName num_params 2 pop 2 pszNodeName [email protected] pstRwFrame [email protected] pstRwFrame [email protected]
                        if
                            [email protected] <> 0x00000000
                        then
                            0A8E: [email protected] = [email protected] + 0x90 // SRwFrame.m_stAtomicList.m_pstNext
                            0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                            [email protected] -= 0x08 // SRpAtomic
                            0AA5: call _rpClumpRemoveAtomic num_params 2 pop 2 pstRpAtomic [email protected] pstRpClump [email protected]
                            0AA5: call _rpAtomicDestroy num_params 1 pop 1 pstRpAtomic [email protected]
                            0AA5: call _rwFrameDestroy num_params 1 pop 1 pstRwFrame [email protected]
                        end
                    end
                end
                0A90: [email protected] = CARCOMPS_EXTRA2 * 0x04 // sizeof(SRwFrame *)
                005A: [email protected] += [email protected]
                0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                if
                    [email protected] <> 0x00000000
                then
                    0A8E: [email protected] = [email protected] + 0x04 // SRpAtomic.m_stObject.m_stObject.m_pstParent
                    0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0
                    0AA7: call_function _rpAtomicClone num_params 1 pop 1 pstRpAtomic [email protected] pstRpAtomic [email protected]
                    0AA7: call_function _rwFrameClone num_params 1 pop 1 pstRwFrame [email protected] pstRwFrame [email protected]
                    0AA5: call _rpAtomicSetFrame num_params 2 pop 2 pstRwFrame [email protected] pstRpAtomic [email protected]
                    0AA5: call _rpClumpAddAtomic num_params 2 pop 2 pstRpAtomic [email protected] pstRpClump [email protected]
                    0AA5: call _rwFrameAddChild num_params 2 pop 2 pstRwChild [email protected] pstRwFrame [email protected]
                    0AA5: call _rpAtomicSetCarMatFX num_params 2 pop 2 iMaterialId 0 pstRpAtomic [email protected]
                    0A8C: write_memory [email protected] size 1 value CARCOMPS_EXTRA2 virtual_protect 0
                end
            end
            while 00E1:  player PAD1 pressed_key LEFTSHOULDER1 // Secondary Fire
                wait 0
            end
        end
    end
    wait 0
end

:HOOK_004C96F5_CModelCars__cloneData
hex
    8B0C24      // mov     ecx, dword ptr [esp]
    8B51 04     // mov     edx, [ecx+SRpAtomic.m_stObject.m_stObject.m_pstParent]
    52          // push    edx
    E8 00000000 // call    rwFrameClone
    8BF8        // mov     edi, eax
    E9 00000000 // jmp     4C9715h
end

:HOOK_004C9763_CModelCars__cloneData
hex
    8B0C24      // mov     ecx, dword ptr [esp]
    8B51 04     // mov     edx, [ecx+SRpAtomic.m_stObject.m_stObject.m_pstParent]
    52          // push    edx
    E8 00000000 // call    rwFrameClone
    8BF8        // mov     edi, eax
    E9 00000000 // jmp     4C9783h
end
Com o tempo estudando o código dele e aprendendo sobre manipulação de memória, especialmente o uso de classes por CLEO, eu estive cada vez mais entendendo o que aquele código fazia, hoje o entendo completamente.

A parte mais importante do código é esta aqui:

Código: Selecionar tudo

0AA7: call_function _rpAtomicClone num_params 1 pop 1 pstRpAtomic [email protected] pstRpAtomic [email protected]
0AA7: call_function _rwFrameClone num_params 1 pop 1 pstRwFrame [email protected] pstRwFrame [email protected]
0AA5: call _rpAtomicSetFrame num_params 2 pop 2 pstRwFrame [email protected] pstRpAtomic [email protected]
0AA5: call _rpClumpAddAtomic num_params 2 pop 2 pstRpAtomic [email protected] pstRpClump [email protected]
0AA5: call _rwFrameAddChild num_params 2 pop 2 pstRwChild [email protected] pstRwFrame [email protected]
Ele simplesmente faz um clone de um RpAtomic e RwFrame, aplica o RpAtomic clonado ao novo RwFrame, adiciona o RpAtomic no RpClump e adiciona o RwFrame como child de outro RwFrame.

Ham?

Esses nomes estranhos são da engine RenderWare. Mas não são tão estranhos assim.

RwFrame é um "frame", no sentido de espaço. Não há uma tradução para português, mas imagine uma caixa com coisas dentro.
Ele é o encarregado de armazenar o conteúdo desta caixa, a sua caixa "filha", a próxima caixa, assim como a matrix que indica a posição do global, local, tamanho, rotação etc.

Código: Selecionar tudo

00000000 RwFrame         struc ; (sizeof=0xA4)
00000000 object          RwObject ?
00000008 inDirtyListLink RwLLLink ?
00000010 modelling       RwMatrix ?
00000050 ltm             RwMatrix ?
00000090 objectList      RwLinkList ?
00000098 child           dd ?                    ; RwFrame *
0000009C next            dd ?                    ; RwFrame *
000000A0 root            dd ?                    ; RwFrame *
000000A4 RwFrame         ends

RpAtomic é "atômico". Eu gosto de imaginar "composição dos átomos", ou seja, é onde o modelo está, com a sua cor, suas propriedades etc. É o objeto visível, e cada RwFrame pode ter mais de um RpAtomic (mas sinceramente é chato trabalhar assim, tanto que VehFuncs e ImVehFt remove isto do jogo para ter melhor controle das peças).
Ele é o encarregado de guardar o lado visível do objeto, como por exemplo um RpGeometry, que como o nome diz, guarda a geometria do RpAtomic, o que inclui o RpMaterialList, que tem vários RpMaterial, que tem RwTexture etc. Até mesmo a mesh do objeto está ali no RpGeometry.
Um RpAtomic também armazena o seu RpClump, o pipeline (se você já abriu o .ini do SkyGfx sabe o que é!), assim como o pai, que a partir do pai nós temos um RwFrame.
Ou seja, a partir de um RpAtomic você tem o controle de todo o modelo.

Código: Selecionar tudo

00000000 RpAtomic        struc ; (sizeof=0x70)
00000000 object          RwObjectHasFrame ?
00000014 repEntry        dd ?                    ; > RwResEntry *
00000018 geometry        dd ?                    ; > RpGeometry *
0000001C boundingSphere  RwSphere ?
0000002C worldBoundingSphere RwSphere ?
0000003C clump           dd ?                    ; RpClump *
00000040 inClumpLink     RwLLLink ?
00000048 renderCallback  dd ?
0000004C interpolator    RpInterpolator ?
00000060 renderFrame     dw ?
00000062 pad             dw ?
00000064 llWorldSectorsInAtomic RwLinkList ?
0000006C pipeline        dd ?                    ; > RxPipeline *
00000070 RpAtomic        ends

RpClump é um "arvoredo", como o nome sugere, ele guarda listas de atômicos, luzes, câmeras etc.

Código: Selecionar tudo

00000000 RpClump         struc ; (sizeof=0x2C)
00000000 object          RwObject ?
00000008 atomicList      RwLinkList ?
00000010 lightList       RwLinkList ?
00000018 cameraList      RwLinkList ?
00000020 inWorldLink     RwLLLink ?
00000028 callback        dd ?
0000002C RpClump         ends
Cada veículo tem um RpClump que pode ser acessado em CVehicle+0x18.
A partir de um RpClump, além de poder acessar todos os objetos contidos, você também pode utilizar funções como: RwFrame *__cdecl CClumpModelInfo::GetFrameFromName(RpClump *clump, char *name) (0x004C5400)
Na qual você envia um nome de um node, por exemplo chassis, e caso encontrado retornará o RwFrame do chassis. É uma função um tanto pesada no entanto.

Agora sim podemos explicar como o Extra - Add.txt funciona, que é o arquivo núcleo do Tuning Mod.
Primeiramente precisamos pegar o RwFrame de destino para adicionar a peça. Por exemplo no chassis ou alguma porta, depende de onde foi selecionado.
Imagem
Esta parte está muito bagunçada, mas foi necessário para cuidar de todas as possibilidades: há vários veículos sem chassis, então eu tenho que pegar o primeiro node, mas há barcos onde o primeiro node é por exemplo o motor... Tem que cuidar disso tudo.

A partir do ID de um modelo nós conseguimos o CBaseModelInfo, que são as informações básicas do modelo. (0x004C5940)

Código: Selecionar tudo

CVehicleModelInfo *__cdecl CModelInfo::GetModelInfo(char *modelName, __int16 *pIndex)

Código: Selecionar tudo

00000000 CBaseModelInfo  struc ; (sizeof=0x20)
00000000 vmt             dd ?                    ; offset
00000004 m_dwKey         dd ?
00000008 m_wUsageCount   dw ?
0000000A m_wTxdIndex     dw ?                    ; index of entry in the TexDictionaryPool
0000000C m_nAlpha        db ?
0000000D m_n2dfxCount    db ?
0000000E m_w2dfxIndex    dw ?
00000010 m_wObjectInfoIndex dw ?
00000012 m_wFlags        dw ?
00000014 m_pColModel     dd ?                    ; offset
00000018 m_fDrawDistance dd ?                    ; float
0000001C m_pRwObject     dd ?                    ; union { RpAtomic * atomic; RpClump * clump; }
00000020 CBaseModelInfo  ends
Perceba que em CBaseModelInfo+0x1C temos o RpClump do modelo, a partir de lá nós conseguimos o controle de todos os RpAtomic e RwFrame do modelo.

Após pegar eu faço checagens para determinar que aquela peça foi instalada pelo Tuning Mod. Eu descubro isto checando se o modelo não tem colisão e se o draw distance é 100.0.
Sendo uma peça do Tuning Mod, será aplicado correções para pintar o material e aplicar pipeline de veículo, assim possibilitando a renderização de specular highlighting, uso de texturas do vehicle.txd etc.

Para pegar o RpAtomic a partir do ID de um modelo é simples, já até mostrei acima, o problema vem quando estamos falando de uma peça de um carro, assim como uma peça extra, mas também foi relativamente simples, só que horrível de se ler:
Imagem

Na nova versão do Tuning Mod (v2.2>) há suporte para multi-nodes, ou seja, em vez de somente 1 RwFrame e 1 RpAtomic, serão vários! E pior: temos que respeitar a ordem hierárquica dos RwFrame.
Isto quer dizer que temos que pegar todos os RwFrame e RpAtomic e tirar um clone de todos, e pior, respeitando a hierarquia!

Para isto é necessário usar recursividade.

Imagem

Deve estar certo, eu fiz ontem (foi um desafio louco) e não testei totalmente.
Acho muito interessante esse código, pois é bem mindblow, especialmente porque o RwFrame de destino se altera a cada child. Agradecimentos ao Fabio pela ideia de recursão com NextChild/NextFrame.

O código que faz o clone do modelo e coloca na peça do carro está aqui:

Imagem

Semelhante ao código original do Wesser.
Perceba que o RwFrame também é posicionado conforme necessário (a posição foi anteriormente calculada, por exemplo se for no chassis a posição inicial da peça será por base da dimensão do modelo do carro, para que ela não apareça enfiada na carroceria). E deixei de usar RwFrameClone pois ele também clona as childs, algo que não desejo, além de causar problemas em clonar o chassis


Gerenciar

Sabendo como isto funciona será muito mais fácil entendermos o resto do mod, por exemplo a interface "Gerenciar".

Para criar a lista de peças eu simplesmente faço uma recursão rodando todos os RwFrame e pegando o nome de cada um deles.

Imagem

Como segurança eu também checo se a quantia de peças passou do limite de 9999. Isto pode acontecer caso causar um loop infinito, mas aparentemente isso nunca aconteceu.

Perceba também que os RwFrame sem nomes eu os corrigi colocando o nome auto seguido de um número. No momento o Tuning Mod é dependente do nome do frame, portanto todos os RwFrame precisam de nome único. É um problemão que eu ainda procurarei uma solução.

Mas algo interessante do funcionamento desta lista de peças é que há um tipo de scroll dependente do item selecionado.

Isto funciona com uma variável que define o início dos nomes para ser mostrados (ou seja, o nome que será mostrado no topo da lista, em vez de sempre começar do início, irá começar de um número variável)

Imagem

E como o valor da variável é definida?

Imagem

Simples assim! Um tanto mindblow, mas é só isto.
Se você precisar fazer uma lista grande com funcionalidade de scroll, está aí uma dica.


Escolher peça

Chegamos na PIOR parte de todo o mod! O arquivo ChoosePart.txt

Esta é a parte mais complicada do mod, a que mais me deu dores de cabeça.

O problema disso tudo é que existe infinitas variações do que pode acontecer — o jogador pode estar selecionando peças por um .ini, ou por ctrl+v, ou por IDs manualmente, ou digitando o nome da peça. É por ID ou é por nome. Ou não é uma peça e sim um veículo, ou então é a peça de um veículo.

As possibilidades de situações é uma árvore com vários galhos e ramos, e temos que cuidar de todas elas. O meu objetivo principal é que o mod nunca cause crash em nenhuma situação, mesmo que o jogador tenha feito coisas erradas, instalado peça com problemas e tudo mais.

De fato antes de publicar o mod eu fiz um código que roda absolutamente todos os modelos do jogo nesta interface, instalando e desinstalando todas as peças, um stress test que demorou um tempão, não deu nenhum crash e aparentemente nenhum vazamento de memória, comprovando que o mod consegue lidar com absolutamente todos os modelos do jogo.

Esta parte do mod não era tão difícil no início, mas quanto mais eu colocava coisas, pior ficava, principalmente ao adicionar a possibilidade de carregar peças de um carro, ou seja, você carregar um carro e em seguida escolher uma peça de um carro para colocar no seu, algo totalmente inesperado para o código do mod, eu nunca imaginaria que eu faria isso, portanto o código encheu de lixo e gambiarras para isto ser possível.

Imagem

O mod também iniciou utilizando arquivos .ini, isto é, você colocava cada peça em uma key do .ini, mas eu decidi melhorar isto, não precisar mais de keys, e para isto me foi necessário na verdade não tratar mais os .ini como arquivos .ini, e sim como um arquivo normal, lendo cada byte.

Imagem

Se trabalhar com a leitura do arquivo já era ruim, imagine lidar com diferentes configurações (como uma configuração onde você diz o veículo e a peça dele, seja o ID ou nome do veículo, ou o nome ou número ordem da peça dele), e lidar com os erros das pessoas para evitar que o mod cause crash em alguma má configuração.

Imagem

Lidar com a leitura do CTRL+V...

Imagem

Lidar com a atualização da peça e caso a atualização tenha sido falha, retornar a peça anterior. Lembrando também que temos que determinar se está mesmo lidando com uma peça e não com um carro no momento.

Imagem

Na lógica parece algo simples "é só criar uma peça ali, tirar ela, colocar outra", mas você vai entrando em dezenas de pequenos detalhes e possibilidades, e tudo vira uma dor de cabeça. Quando percebe o seu código já tem quase 2 mil linhas.

No início o código tava bonito, com o tempo foi cada vez estragando mais, mas pelo menos está em ótimo funcionamento, e é melhor não mexer com quem está quieto!

Mesmo que seja interessante, não o use como referência para uma boa lógica. De fato o código tem que ser reescrito com lógica repensada (de novo, eu já o reescrevi várias vezes).


Instalação da peça

Finalmente, uma das únicas partes do mod que eu tenho orgulho e encorajo você de dar uma olhada no código-fonte.
Não é perfeita, mas a lógica ficou muito boa.

Imagem

O código da instalação da peça é centenário: O código é o mesmo desde a Loja BMS Sound em 2012!!!

Imagem

Obviamente super diferente, mas ainda com as semelhanças e mesma lógica: if then else em cada tecla, começando da mais complexa para a mais simples, testando cada combinação a partir da outra tecla.

Imagem

Perceba que em vez de usar a alteração da variável direto no código, eu preferi usar um scm_func, pois lá eu tenho melhor controle do valor a ser aumentado ou diminuído.

Imagem

Note que a velocidade é baseada num cálculo médio de delta-time, assim a velocidade se torna independente de FPS, mas o cálculo de delta-time não pode ser calculado enquanto a peça está se movendo. O código acima em comparação com a BMS Sound é o código onde a média de delta-time é calculada, perceba que só se calcula enquanto não está pressionando nenhum botão e tenha uma boa quantia de frames calculados somente 10 frames já é o bastante).
Isto é, se você soltar as teclas com 30 FPS, o mod calculará a média de delta-time a cada 10 frames para definir a velocidade de movimento e assim a velocidade não ser alta ou devagar caso o seu FPS esteja alto ou baixo.

São coisas que quando você vai criar um mod nem imagina que terá que fazer, e quando se depara com o problema é obrigado a usar a criatividade.

Enfim, e sobre a interface, também mostrado acima, há funções que definem o texto de cada tecla.
Repetindo uma parte aqui:

Imagem

Ou seja, ao segurar X, o texto onde fica a ferramenta da tecla S é alterado para o texto de ID 322, na qual no Tuning Mod.fxt é _TM_322 [‎S‎] Reset. Tamanho. E no Z é aplicado -1 para mostrar nada.
É deste modo que a interface de instalação se altera a cada tecla pressionada.

Após todo o processamento dos comandos, ao renderizar a interface, os valores guardados são lidos e mostrados conforme.

Imagem

Outra coisa interessante da instalação da peça é a deformação: é nada mais nada menos que a alteração da matrix do RwFrame.
As rotações funcionam por quaternion, ou seja, não são XYZ e sim XYZW. O modo de deformação é nada mais nada menos que a alteração de cada elemento do quaternion.


Salvar e carregar

Oh nãooooooo, isto não!!!

A parte mais chata do desenvolvimento do Tuning Mod: salvar e carregar os veículos! Sem dúvida alguma, é o que mais atrapalha em adicionar novas funcionalidades ao mod, tudo o que eu tenho que adicionar de novo também tenho que adicionar no salvamento e carregamento do mod.

Imagem

Mas enfim, falando sobre o funcionamento, foi criado com base num código de salvar e carregar veículos por arquivos criado pela Thayná. Eu simplesmente usei o mesmo código, incluindo informações adicionais sobre peças etc.

Todos os arquivos têm que ser salvos e lidos exatamente do mesmo jeito, byte por byte. Isto é, foi criado de maneira linear, se pular ou faltar 1 byte, todo o resto do carregamento irá se estragar.
A solução para isto seria eu fazer um header no arquivo, assim  o header diria onde cada informação está (onde está armazenado as cores do veículo, onde está a suspensão, o tamanho da roda, as peças adicionais, os nomes das peças, as cores das peças etc etc...). Mas até hoje não tive coragem de fazer isso, quem sabe logo farei.

O modo atual de funcionamento, como se pode ver, fica incompatível com carros salvos em versões antigas. Por exemplo se eu coloco uma informação adicional entre a cor e paintjob, arquivos .tmv da versão antiga ficarão incompatíveis. Para resolver eu utilizei uma gambiarra: guardar um número de versão do arquivo logo no primeiro byte (ou seja, para saber qual a versão do arquivo .tmv, basta você ler o primeiro byte do arquivo). Assim sabendo qual foi a versão do Tuning Mod que salvou o arquivo, eu posso lidar com isso pulando os bytes adicionais da nova versão.

Fazer isso polui muito o código, não faça, após 2 versões o seu código já ficará uma merda de se ler e lidar, prefira utilizar header!

Foi difícil eu fazer esse código ficar estável, mas quando consegui eu implementei salvamento automático, que foi muitíssimo bom, pois agora o mod podia crashar que o jogador não iria mais perder sua criação! O salvamento automático é chamado em todas as mudanças de tela, quase nem se percebe, mas há um "Autosaving..." no topo da tela.

Como eu disse, fazer o salvamento e carregamento das informações é realmente muito chato, e também difícil quando o assunto são as peças adicionais: cada peça adicionada é necessário ter as informações do que ela é, de onde ela veio etc para guardar no arquivo e refazer todo o processo de readicionar aquela peça, e pior, respeitando a posição dela! Pois a posição que ela se encontra é importante, dependente da ordem de uma peça com alpha por exemplo poderá esconder o carro atrás da peça (é assim que o Holes funciona!) e devido as peças serem adicionadas em ordem inversa no veículo (pense um pouco, ao pegar as peças, salvar e carregar elas voltarão em uma ordem inversa), eu tive até que ler todas as peças, guardar na memória para re-adicioná-las em ordem invertida no veículo.

Agora a minha atual preocupação está sobre as peças multi-nodes, afinal, não será só uma peça, e sim uma peça com várias outras peças filhas! E precisará ter a funcionalidade de alterar o nome da peça filha no Gerenciar. Eu ainda estou pensando em como diabos eu vou resolver esses problemas.

Ainda tem o GSX do Fabio que possibilita salvar os dados na garagem, eu poderia usar, mas é mais outro problema de salvar/carregar para eu enfrentar (o que um já era chato demais). E eu não quero deixar de usar arquivos para usar somente o GSX pois compartilhar os carros por arquivos é legal.

Mas santa porra, parem de me pedir para salvar o carro em .dff! Isso é totalmente sem noção, nem o ZModeler salva um .dff direito, imagine eu, in-game, por cleo. Para você ter noção, o jogo não armazena nem o nome do arquivo .dff, ou seja, nem o nome do .dff do carro é possível saber in-game e você vai querer que eu salve um arquivo .dff inteiro in-game? ("então como que eu digito o nome do .dff do carro e sabe?" é porque é utilizado hash).


Ainda tem muita coisa o que explicar, mas acho que o principal já está aí. Podem perguntar nos comentários por mais informações sobre algo já dito ou não dito aqui.
 
por Junior_Djjr
30 Jun 2018, 13:52
Fórum: Tutoriais
Tópico: Como o Tuning Mod v2 funciona?
Respostas: 6
Exibições: 1583
Gênero:

Como o Tuning Mod v2 funciona?

v2, não v3
Eu ainda não atualizei este tópico para falar sobre a versão 3. Mas no download virá o source code pra v3, e eu fiz um vídeo mostrando todo o código:


Atenção
Este tópico é técnico e não serve muito bem para leigos, mesmo que ainda dá para entender algo mesmo quem não sabe de cleo.

Não é bem uma referência de qualidade
Eu criei o Tuning Mod v1 e resolvi criar o v2 pois o v1 era muito mal programado. No entanto, o v2 também foi mal programado e organizado!
A v3 dei uma melhorada, mas ainda é um frankstein. O código é grande demais para recriar do zero, então há partes bem programadas, enquanto outras são horríveis.

Um velho fumando um cigarro de palha
O Tuning Mod v2 foi criado totalmente no Sanny Builder, com somente algumas coisas em .asi, mas 99,9% do mod é em cleo, e pior, pelo Sanny Builder. O Tuning Mod v3 teve mais algumas coisas no .asi e o script de tuning nativo (como Transfender etc) é em GTA3script.
Na época do v2 ainda não existia GTA3script e muito menos MoonLoader, e eu também não sabia .asi, portanto o código foi criado da pior maneira possível! Low level (no sentido do trabalho, não do código fonte) e difícil de compreender. Foi realmente um gigante desafio, e continua sendo pois devido a má organização (não só minha, mas também com algumas culpas da limitação do Sanny Builder) é difícil atualizá-lo, sempre causa dores de cabeça quando tento mexer. Você vai entender.
Sanny Builder também foi o motivo das variáveis quase não serem nomeadas. No SB é como se as variáveis fossem um só escopo (nem isso na verdade), portanto, imagine só um código de mais de 40 mil linhas compartilhando os mesmos nomes de variáveis em diferentes ocasiões! Eu teria que usar nomes com prefixos em cada parte do código. Por isso eu não pude usar variáveis nomeadas, o que deixou o código ainda pior.

Olá
Eu só abri e comecei a escrever este tópico como se estivesse conversando, eu quase nem revisei, portanto não espere nada demais.
Você pode baixar o código-fonte do Tuning Mod v3 aqui.
Mas as explicações abaixo são para a versão v2.2, portanto são diferentes e falta muita coisa.

  


Eu decidi fazer um texto explicando o funcionamento do Tuning Mod pois é muito importante compartilhar conhecimento. Acredito que seria interessante mais modders fazendo tópicos como este.

Este tópico não é exatamente para ensinar como fazer certa coisa, acredito que o objetivo é mais sobre design, ou seja, como pensar e aplicar certas lógicas, além de curiosidades de como certas coisas foram feitas.
Tanto que eu só estou postando imagens de partes do código em vez do código em si, pois não importa as letrinhas e números, o que importa mais é como aquilo foi pensado, o design.

Primeiramente a organização do código é totalmente separada em arquivos, afinal, ninguém merece mexer num código de mais de 40 mil linhas no mesmo arquivo, ainda mais no Sanny Builder.

Imagem

Assim eu deixo o Tuning Mod (Junior_Djjr).txt aberto só para compilar, e nas outras guias vou abrindo os outros arquivos, após editar, eu salvo o arquivo, volto nesta guia e pressiono F6 para compilar.

Há vários arquivos com nomes "Const" de constantes, onde uso como se fosse arquivos .h de programações como C.

Imagem

Estão super mal organizados, mas não me importo pois são arquivos onde eu só abro, adiciono algo e fecho.
Na imagem acima tem comentários com outro endereço na frente, é o equivalente no GTA Vice City para quando eu fizer o Tuning Mod no GTA VC. Eu anotei faz muito tempo com ajuda do LINK/2012, acredito que não preciso mais pois o .idb do GTA VC hoje está bem completo já.


Inicialização

O arquivo Script init.txt é a inicialização do mod.

Imagem

Devido ao mod ser muito usado e complexo, eu acho muito importante o próprio mod ter como manusear erros, assim informando ao jogador que ele instalou o mod incorretamente — por favor, mais mods assim!

É pra isso que serve o LoadFiles.txt, um arquivo de mais de 300 linhas só para carregar umas configurações e checar se o mod está corretamente instalado.

Imagem

Eu recomendo altamente que você faça algo assim em seus mods. Note que eu fiz um loop de alguns segundos mostrando o texto na tela sem parar, para realmente forçar o texto a aparecer, não deixar que o jogador deixe de ver a mensagem de erro devido a outros mods também mostrando outros textos.

Assim como a minha funçãozinha de mod multilíngue. É muito fácil implementar mais de um idioma no mesmo código, seria bom mais pessoas fazerem isso. Outro lado bom de fazer desta maneira é que você já vai traduzindo o mod enquanto o cria, o que é muito chato traduzi-lo no final — evite deixar as partes mais chatas para o final!!! É assim que projetos são cancelados!!! Tente deixar as partes legais para depois e cuide das chatas antes, isso ajudará sua produtividade de diversas formas.

E assim chegamos no Main - Check.txt, onde há um loop que fica rodando durante o gameplay fora do mod.
O conteúdo do loop é simples, se você está fora de um carro a única coisa que irá rodar é o comando T+L para abrir o spawner (viu só quem fica falando que o Tuning Mod tira FPS do jogo só de ter instalado??). E caso você estiver dentro de um carro o mod irá checar se o handling é independente (IndieVehHandlings.cs), pegar o tipo de veículo (se é carro, moto, bicicleta etc), pegar o trailer e com esses dados definir qual local o veículo tem que estar para abrir a garagem. Claro, há também a checagem da tecla TAB para mostrar as informações na tela.
Imagem 

Utilizando o comando de checar se está no local, parado com um carro (eu poderia ter usado a versão de carro em vez de ped, né), com sphere 1 para mostrar a marca vermelha, assim entra numa sequência de ativação e desativação do mod.

Primeiro guarda o tipo de veículo (uma função um pouco maior do que a anterior, guardando todas as informações necessárias sobre que veículo é este). Guarda as dimensões do modelo do veículo (útil para por exemplo definir a distância de zoom do carro, posição da peça no chassis etc). Também marca o veículo como "Tuning Moded", indicando que foi um veículo utilizado pelo Tuning Mod, sendo útil para certas coisas (inclusive mods terceiros).

E chegamos no Main - Menu.txt, onde, como o nome diz, é o menu principal do mod.
Começa com armazenamento do local, veículo, carregamento de .txd e a introdução do mod (caso aplicável). E antes de tudo, a função Lock que é a função principal onde o veículo é "travado" no mapa.

Imagem

Acredite, essa função já foi reescrita umas 5 vezes, é uma das partes que mais me deram dores de cabeça no mod — algo que era para ser super simples.

Vou querer dar grande atenção à ela pois vai lhe ser muito útil para mods do gênero:
Logo no início temos várias desativações relacionadas ao gameplay para evitar coisas inexperadas:

Imagem

Em seguida, dependente do local o veículo é colocado em tal lugar de certo modo:

Imagem

Perceba que foi necessário umas gambiarras para fazer isso funcionar como devia. O problema mais chato que tive foi do veículo aparecendo escuro flutuando travado no ar, para tentar resolver eu tinha feito códigos absurdos, mas acredito que consegui uma solução simples já.

O código contrário (ou seja, de sair) é semelhante. Algo importante é que eu fiz um backup do CVehicle+CreatedBy (0x4A4, 1 byte) do veículo e troquei para 2 (para dizer que o veículo é controlado por script e assim evitar que o jogo o delete).

Imagem

Sem isto o jogo facilmente causa crash ao entrar e sair da garagem, até mesmo durante a instalação de peças ao mover a câmera.
Ao sair da garagem o mod re-adiciona o backup do CreatedBy, isto é, se o veículo era um carro aleatório qualquer, ele voltará a ser um, e se ele já era controlado por script, também voltará a ser, assim não causando problemas com missões e outros mods.

Isto é algo muito importante para seus mods, se você quiser marcar um veículo já existente como "necessário por script" para evitar que ele seja deletado pelo jogo, você não pode em seguida desmarcar (01C3: em Sanny Builder; MARK_CAR_AS_NO_LONGER_NEEDED em GTA3script), e sim voltar ao estado anterior, senão se o carro já era usado por missão ele se transformará num carro aleatório e dará missão fracassada ou atrapalhará o uso de algum mod, por exemplo causando crashes.

Voltando ao menu, bem, você já deve ter notado que quando você entra no menu do Tuning Mod a tela se abre como se fosse olhos abrindo.
Isso foi feito super facilmente:
Imagem
Sim, só isso.

O processamento do menu é separado em arquivos, e este loop checa o tipo de veículo e mostra conforme necessário.
Imagem

Esta parte foi bem merda de ser feita, pois é duplicação de código, mas sinceramente não encontrei uma maneira melhor e ainda agradável.

Há diversos arquivos de menu separados, cada um com seus itens e comandos por base do tipo de veículo.

Para determinar qual item está selecionado eu utilizei variáveis como se fosse uma tabela: uma variável informando qual linha está selecionada na primeira coluna, outra na segunda etc. Eu pensava ser uma má solução, mas na prática eu gostei, acredito ser um bom modo de fazer menus pop-ups.


Funcionamento do menu

Toda a interface do mod foi criada utilizando um sisteminha de interface que eu criei chamado Shine GUI. Não é tão simples para leigos mas é completamente personalizável e dinâmica.

Para ter a animação quando um item é selecionado eu utilizo um código que calcula uma variável usada para tamanho e posição vertical e renderizo o item usando ela a cada frame, juntamente com a variável de "progresso" sendo incrementada, assim causando uma animação aumentando de tamanho a cada frame.
Imagem
Uma outra variável Processed* informa se o tal item terminou de ser processado, ou seja, se a animação já chegou ao tamanho necessário, assim o texto do item será mostrado e não terá mais cálculo de tamanho, o item será mostrado como algo estático.

O loop for to de cada item é o número da entrada GXT do arquivo .fxt do mod, como explicado no tutorial do Shine GUI. É deste modo que cada item da coluna é renderizado verticalmente.

A posição de cada item, tamanho etc estão todos em arquivos Const para fácil alteração.

Em outro arquivo há o processamento dos comandos, ou seja, é onde fica a quantia de itens de cada coluna.

Imagem

Eu odeio este arquivo!
São 350 linhas disso, só nos carros (ainda tem a mesma coisa para motos, aviões, trailers, barcos...), eu sinceramente não sei outra solução boa e agradável de se fazer.

E continua, ainda temos o arquivo que transforma a junção das variáveis indicando cada linha de cada coluna em um número final, além de uns processamentos de interface.

Imagem

Também odeio este arquivo.
Mas tudo dá para ficar pior:

Imagem

Bem que o Sanny Builder poderia ter um switch, né?

No código principal do menu temos isto, que aplica tal coisa caso tal item do menu esteja selecionado e você tenha pressionado Y.

Note também que a mesma variável que diz qual item está selecionado, é a variável que diz qual ID da entrada GXT está selecionada para colocar o efeito de item selecionado no momento em que o tal item for renderizado.

Imagem

O tal efeito de item selecionado é na verdade +1 no ID da formatação.
Ou seja, se a formatação da letra do menu é 10, o ID 11 é a formatação do item de menu selecionado, assim eu checo se o atual item para mostrar é o mesmo ID do atual item selecionado e adiciono +1 na variável do ID da formatação.
Foi uma boa gambiarra.

No momento há mais de 400 linhas de códigos de diferentes formatações de texto para diferentes itens, botões, cabeçalhos, descrições, janelas etc...

Imagem

Prefiro nem comentar...

Mas se nota algo interessante ali, em certas formatações eu utilizo uma cor RGB anteriormente guardada ao pegar o tipo de veículo.

Ah, agora você entendeu como que o Tuning Mod tem diferentes cores de interface em cada tipo de veículo, né?

Imagem

Algo interessante é como o efeito de pulsar do item selecionado funciona.
Vou deixar o código aqui para vocês usarem:

Código: Selecionar tudo

:GUI_ItemMenuActive_PulseColor_Update {ResetPulse [email protected]}
0AC6: [email protected] = label @GUI_Memory_ItemMenuActive_PulseColor offset
0AC6: [email protected] = label @GUI_Memory_ItemMenuActive_PulseColor_Step offset

if [email protected] == 1
then
    0A8C: write_memory [email protected] size 4 value 255.0 vp 0
    0A8C: write_memory [email protected] size 4 value 1 vp 0
else
    0A8D: [email protected] = read_memory [email protected] size 4 vp 0
    0A8D: [email protected] = read_memory [email protected] size 4 vp 0

    if [email protected] == 1
    then //Down
        007F: [email protected] -= frame_delta_time * ItemPulseSpeed
        if [email protected] < 180.0
        then
            [email protected] = 180.0
            [email protected] = 2
        end
    else //Up
        0079: [email protected] += frame_delta_time * ItemPulseSpeed
        if [email protected] > 255.0
        then
            [email protected] = 255.0
            [email protected] = 1
        end
    end

    0A8C: write_memory [email protected] size 4 value [email protected] vp 0
    0A8C: write_memory [email protected] size 4 value [email protected] vp 0
end
0AB2: ret 0
É bem simples na verdade.
Assim, basta ler o valor da label GUI_Memory_ItemMenuActive_PulseColor para retornar um valor entre 180.0 e 255.0 por base do progresso do pulsar.
Para o pulsar ser processado é necessário chamar a função acima em loop.

Código: Selecionar tudo

0AB1: call_scm_func @GUI_ItemMenuActive_PulseColor_Update 1 ResetPulse false
Use ResetPulse true para o pulso ser resetado. É importante para quando você altera o item selecionado, o pulso tem que ser reiniciado para dar um efeito conveniente.


Tuning

Vamos supor que escolhemos a instalação de um tuning, como turbo.

Devido a merdas do jogo, como crashes aleatórios após alterar a velocidade do carro, foi necessário eu alterar as propriedades físicas, como inércia e resistência do ar para fazer a velocidade aumentar...

Imagem

Mas o que é interessante mesmo é como diabos eu fiz para mostrar as barrinhas de velocidade etc do carro mostrarem como elas serão após instalar o turbo (ou seja, a prévia de alteração de velocidade após instalar).

Exato, eu lhe pergunto, como diabos eu fiz? Eu também não sei.

Enquanto escrevo isto estou tentando entender. É um dos poucos sistema do TM v2 que continuaram iguais ao v1, pois eu peguei trauma e não quis recriar.

É sério, na época eu passei uma semana tentando fazer essa merda funcionar direito. Hoje se eu tentasse seria mais fácil, mas estou com trauma, se está funcionando bem é melhor eu nem mexer mais nisso!

Basicamente eu faço um reverso da instalação para simular:

Imagem

Veja este código e o código de cima, eu não sei explicar a lógica, mas ta aí.

Assim quando mostrar os stats, a cada cálculo e desenho de cada barrinha, eu mostro a barrinha calculada no momento e a barrinha calculada no código acima, uma em cima da outra, onde a barrinha prévia vem por baixo e colorida.

Imagem

Cada tuning instalado é armazenado no handling adicional do carro que funciona pelo IndieVehHandlings. Eu ainda pretendo fazer um tutorial desta API aqui, mas ainda penso em recriar o IndieVehHandlings em .asi...
Bem, assim por exemplo se eu instalo um turbo, será ligado um bit nas flags das peças instaladas para dizer que o tal turbo está instalado.
Isto é útil para saber se a tal peça já está instalada, assim como mods terceiros podem por exemplo adicionar um relógio de turbo no hud caso tenha um turbo instalado, coisa do tipo.
Para entender o funcionamento, no momento, basta abrir o IndieVehHandlings.cs com o Sanny Builder, lá há toda a documentação embutida.


Mais

A interface "Mais" foi criada para adicionar funcionalidades variadas de modo fácil ao mod. E eu falhei! Adicionar uma nova funcionalidade lá ficou bem chatinho. Na verdade, a chatice é igual ao menu principal (lá também está chato adicionar novos itens).
No entanto, não está tão ruim assim.

O funcionamento é semelhante ao menu principal, só que agora com novo design e somente uma coluna.

A cada item selecionado há códigos para a instalação do item.

Imagem

Ou seja, é algo até bem agradável de se trabalhar, o lado ruim é adicionar o item à lista de itens válidos, afinal, vamos lembrar que o Tuning Mod não é só para carros, mas também para motos, aviões, barcos, quadriciclo e monster truck. Tudo tem que ser manuseado para mostrar os itens corretos para cada tipo de veículo.

Imagem


Na parte 2 abaixo entraremos em detalhes mais avançados:
 

Voltar para “Como o Tuning Mod v2 funciona?”