Jump to content

EXE editing & programming issues


MrFlibble

Recommended Posts

Maybe Death Hand Missile behaviour is different:

src/structure.c

void Structure_ActivateSpecial(Structure *s){    House *h;    if (s == NULL) return;    if (s->o.type != STRUCTURE_PALACE) return;    h = House_Get_ByIndex(s->o.houseID);    if (!h->flags.used) return;    switch (g_table_houseInfo[s->o.houseID].specialWeapon) {        case HOUSE_WEAPON_MISSLE: {            Unit *u;            tile32 position;            position.s.x = 0xFFFF;            position.s.y = 0xFFFF;            g_var_38BC++;            u = Unit_Create(UNIT_INDEX_INVALID, UNIT_MISSILE_HOUSE, s->o.houseID, position, Tools_Random_256());            g_var_38BC--;            g_unitHouseMissile = u;            if (u == NULL) break;            s->countDown = g_table_houseInfo[s->o.houseID].specialCountDown;            if (!h->flags.human) {                PoolFindStruct find;                find.houseID = HOUSE_INVALID;                find.type    = 0xFFFF;                find.index   = 0xFFFF;                /* For the AI, try to find the first structure which is not ours, and launch missile to there */                while (true) {                    Structure *sf;                    sf = Structure_Find(&find);                    if (sf == NULL) break;                    if (House_AreAllied(s->o.houseID, sf->o.houseID)) continue;                    Unit_LaunchHouseMissile(Tile_PackTile(sf->o.position));                    return;                }                /* We failed to find a target, so remove the missile */                Unit_Free(u);                g_unitHouseMissile = NULL;                return;            }[...]

There is only check:


if (House_AreAllied(s->o.houseID, sf->o.houseID)) continue;

 

and before should do the same that we have seen previous post about Targets.

Indeed in original game it's not expected to have a Fremen base.
I think that it's possible to fix it on C++, on EXE must look about room bytes. :)

Link to comment
Share on other sites

Testing in-game about dynasty report, actually sometimes AI (depends what structure it finds first) launches Death Hand Missile against Fremen structures (on my modified EXE, even or Atreides structures, because of new exception that I added for bilateral alliance).

I tried playing Mercenary, since their ID is the last (05), enemy AI find first ID 03 (Fremen) structures to target by Death Hand Missile.
 
There is no room bytes to add code in DeathHand creation chunk of code, so I simply swapped macro-checks in isFriendly? function:
 
bool House_AreAllied(uint8 houseID1, uint8 houseID2)
{
        if (houseID1 == HOUSE_INVALID || houseID2 == HOUSE_INVALID) return false;

        if (houseID1 == houseID2) return true;
        
       
// Human Check is made BEFORE than other exceptions
        if (houseID1 != g_playerHouseID && houseID2 != g_playerHouseID) return true;
        
        
        if (houseID1 == HOUSE_FREMEN || houseID2 == HOUSE_FREMEN) {
                return (houseID1 == HOUSE_ATREIDES || houseID2 == HOUSE_ATREIDES);
        }
        

        if (houseID1 == HOUSE_ATREIDES || houseID2 == HOUSE_ATREIDES) {
                return (houseID1 == HOUSE_FREMEN || houseID2 == HOUSE_FREMEN);
        }
        
        return false;

}

 
In yellow check that it's positioned BEFORE the Fremen/Atreides & Atreides/Fremen exceptions, so we can fix dynasty reported "bug". ;)

I've updated previous posts.

 

 

 

EDITED: DISCARD CHANGES -> WRONG

Link to comment
Share on other sites

Wouldn't do for me to get all the credit, without segra's database I'd be near helpless in many things. He did a very thorough job looking through the code, identifying most of the functions - basically, all the groundwork without which any understanding of the game would hardly be possible.

Also the OpenDUNE guys did a truly great job, although I personally never read their code for modding purposes but TrueBrain helped me with several issues I wouldn't have figured out myself.

And let's not forget Nyerguds' excellent tool which simplifies a lot of things. And whatever information I provided to Nyer to base the early versions of the editor on was discovered by using the older "cheat program" (basically an EXE editor for v1.0 only) written by a Jens Defendorf (you can still get it here: D2CHT10.ZIP).

Link to comment
Share on other sites

Yes indeed "all of you" means all guys that you have mentioned including yourself of course.

Each one of you guys has contributed to discovered very essential things about modding to this timeless game. :)

 

I would like to see again segra at work on asm code debugging... :)

 

I've found C++ OpenDune code very readable and handy to undestand some particular strange operations about original code.

Thanks to TrueBrain and other guys on OpenDune project.

Link to comment
Share on other sites

Now talking about which I consider a real bug on Saboteur movement.

https://github.com/OpenDUNE/OpenDUNE/issues/138

The issue is that he detonates the explosion on some tiles if movement is faster (depending to game speed and terrain type) than ciclying of funtion:

bool Unit_Move(Unit *unit, uint16 distance){    const UnitInfo *ui;    uint16 d;    uint16 packed;    tile32 newPosition;    bool ret;    tile32 currentDestination;    bool isSpiceBloom = false;    bool isSpecialBloom = false;    [...]        ret = (unit->distanceToDestination < distance || distance < 16) ? true : false;        if (ret) {            if (ui->flags.isBullet) {            [...]            } else if (ui->flags.isGroundUnit) {            [...]                if (unit->o.type == UNIT_SABOTEUR && (Map_GetLandscapeType(Tile_PackTile(newPosition)) == LST_WALL                            || (unit->targetMove != 0 && Tile_GetDistance(unit->o.position, Tools_Index_GetTile(unit->targetMove)) < 32))) {                    Map_MakeExplosion(4, newPosition, 500, 0);                    Unit_Free(unit);                    return true;                }                [...]

I don't like personally to use an unit that after you do "Move" command, you always set "Stop" command before it arrives on destination to avoid that it explodes...
I remember that Amiga version of Dune 2, Saboteur never exploded on free tiles, so I think this is the right behaviour anyway.

To fix this bug, I simply changed jge check to jmp unconditional:

seg031:06FE                   loc_28A5E:                                                  ; CODE XREF: unitMoveMapTileCheck:loc_2...
seg031:06FE                                                                               ; unitMoveMapTileCheck+6DBj
seg031:06FE C4 5E 06                          les     bx, [bp+pUnitGamePtr]               ; Load Full Pointer to ES:xx
seg031:0701 26 80 7F 02 06                    cmp     es:[bx+_unitGame.typeIndex], 6      ; Saboteur
seg031:0706 75 73                             jnz     short loc_28ADB                     ; Jump if Not Zero (ZF=0)
seg031:0708 C4 5E 06                          les     bx, [bp+pUnitGamePtr]               ; Load Full Pointer to ES:xx
seg031:070B 26 83 7F 56 00                    cmp     es:[bx+_unitGame.scriptObjectHolding], 0 ; Compare Two Operands
seg031:0710 74 4E                             jz      short loc_28AC0                     ; Jump if Zero (ZF=1)
seg031:0712
seg031:0712                   loc_28A72:                                                  ; Load Full Pointer to ES:xx
seg031:0712 C4 5E 06                          les     bx, [bp+pUnitGamePtr]
seg031:0715 26 FF 77 56                       push    es:[bx+_unitGame.scriptObjectHolding] ; objectIndexType
seg031:0719 9A C2 01 80 24                    call    scriptObjectGetMapTileXY            ; Call Procedure
seg031:071E 59                                pop     cx
seg031:071F 52                                push    dx                                  ; buildingPosY
seg031:0720 50                                push    ax                                  ; buildingPosX
seg031:0721 C4 5E 06                          les     bx, [bp+pUnitGamePtr]               ; Load Full Pointer to ES:xx
seg031:0724 26 FF 77 0C                       push    word ptr es:[bx+0Ch]                ; unitPosY
seg031:0728 26 FF 77 0A                       push    word ptr es:[bx+0Ah]                ; unitPosX
seg031:072C 9A B4 00 48 1D                    call    distanceCalculate                   ; Call Procedure
seg031:0731
seg031:0731                   loc_28A91:                                                  ; Add
seg031:0731 83 C4 08                          add     sp, 8
seg031:0734 3D 20 00                          cmp     ax, 20h ; ' '                       ; Compare Two Operands
seg031:0737 7D 27                             jge     short loc_28AC0                     ; Jump if Greater or Equal (SF=OF)

seg031:0737 EB 27                             jmp     short loc_28AC0                     ; Jump
seg031:0739
seg031:0739                   loc_28A99:                                                  ; CODE XREF: unitMoveMapTileCheck+76Dj
seg031:0739 33 C0                             xor     ax, ax                              ; Logical Exclusive OR
seg031:073B 50                                push    ax                                  ; objectTypeIndex
seg031:073C B8 F4 01                          mov     ax, 1F4h
seg031:073F
seg031:073F                   loc_28A9F:                                                  ; HitPointsMax
seg031:073F 50                                push    ax
seg031:0740 FF 76 FC                          push    [bp+unitPosY]                       ; unitPosY
seg031:0743 FF 76 FA                          push    [bp+unitPosX]                       ; unitPosX
seg031:0746 B8 04 00                          mov     ax, 4
seg031:0749 50                                push    ax                                  ; int
seg031:074A 9A 08 00 00 15                    call    sub_15008                           ; Call Procedure
seg031:074F 83 C4 0A                          add     sp, 0Ah                             ; Add
seg031:0752 FF 76 08                          push    word ptr [bp+pUnitGamePtr+2]
seg031:0755 FF 76 06                          push    word ptr [bp+pUnitGamePtr]          ; unitGamePtr
seg031:0758 9A 68 05 ED 1D                    call    unitGameDestroy                     ; Call Procedure
seg031:075D E9 98 F9                          jmp     loc_28458                           ; Jump
seg031:0760                   ; ---------------------------------------------------------------------------
seg031:0760
seg031:0760                   loc_28AC0:                                                  ; CODE XREF: unitMoveMapTileCheck+704j
seg031:0760                                                                               ; unitMoveMapTileCheck+72Bj
seg031:0760 FF 76 FC                          push    [bp+unitPosY]                       ; posY
seg031:0763 FF 76 FA                          push    [bp+unitPosX]                       ; posX

 

20h corresponds to 32 decimal, value looked in C++ function.

I think so the Saboteur is handier and useful. ;)

Link to comment
Share on other sites

It definately is possible for non-Atreides AI tanks to crush supposedly allied Fremen infantry.  It takes some setting up in the original game, but it could become common if Fremen is a major house.

 
Death Hand targetting logic simply goes by index rather than by house.  You see the problem in Dune 2 eXtended, but not Super Dune II Classic, when playing against the same houses.
 
Your new logic isn't any better:
g_playerHouseID = A;House_AreAllied(H, F) -> true;g_playerHouseID = F;House_AreAllied(H, A) -> true;g_playerHouseID = H;House_AreAllied(S, A) -> false;

 

Try to implement something like this:
bool House_AreAllied(uint8 houseID1, uint8 houseID2){    uint8 playerHouse = g_playerHouseID;    if (playerHouse == HOUSE_FREMEN) playerHouse = HOUSE_ATREIDES;    if (houseID1 == HOUSE_FREMEN) houseID1 = HOUSE_ATREIDES;    if (houseID2 == HOUSE_FREMEN) houseID2 = HOUSE_ATREIDES;    return ((houseID1 == playerHouse) == (houseID2 == playerHouse));}
Link to comment
Share on other sites

Yes indeed "all of you" means all guys that you have mentioned including yourself of course.

Each one of you guys has contributed to discovered very essential things about modding to this timeless game. :)

Uhh, silly me, I misread your post somehow as "thanks to you for all" or something :-/ That's what happens when you are sleepy :D
Link to comment
Share on other sites

@dynasty: your function it's smart and simple. ;)

The last check it's absolutely cool and ingenious, it isn't so banal to think and find out:

 

return ((houseID1 == playerHouse) == (houseID2 == playerHouse));

 

If there was enough room bytes, teorically you could create a series of exceptions for all factions on same EXE:

 

 if (playerHouse == HOUSE_FREMEN) playerHouse = HOUSE_ATREIDES;
 if (houseID1 == HOUSE_FREMEN) houseID1 = HOUSE_ATREIDES;
 if (houseID2 == HOUSE_FREMEN) houseID2 = HOUSE_ATREIDES;

 

 if (playerHouse == HOUSE_MERCENARY) playerHouse = HOUSE_ORDOS;
 if (houseID1 == HOUSE_MERCENARY) houseID1 = HOUSE_ORDOS;
 if (houseID2 == HOUSE_MERCENARY) houseID2 = HOUSE_ORDOS;

 

 if (playerHouse == HOUSE_SARDAUKAR) playerHouse = HOUSE_HARKONNEN;
 if (houseID1 == HOUSE_SARDAUKAR) houseID1 = HOUSE_HARKONNEN;
 if (houseID2 == HOUSE_SARDAUKAR) houseID2 = HOUSE_HARKONNEN;

 

For example assuming a classic dual alliance between all 6 factions.

Unfortunately there is not enough room bytes in EXE for all these cases.

However by tests done it seems there are no side effects on solution suggest by dynasty.

 

Today I post all fixed asm. :)

Link to comment
Share on other sites

Translating on asm this modified function (1 of 4):
 
hp = House_Get_ByIndex(g_playerHouseID);
    ui = &g_table_unitInfo[unit->o.type];
    h = House_Get_ByIndex(houseID);
    houseIDBit = (1 << houseID);

    if (
(houseID == HOUSE_ATREIDES || houseID == HOUSE_FREMEN) && unit->o.type != UNIT_SANDWORM) {
        houseIDBit |= (1 << HOUSE_FREMEN)
+(1 << HOUSE_ATREIDES);
    }

    if ((unit->o.seenByHouses & houseIDBit) != 0 && h->flags.isAIActive) {
        unit->o.seenByHouses |= houseIDBit;
        return;
    }

 
Because of too little room bytes available on this step, I had to fix it using CX register instead pointer regards houseIndex being CL already valued on that ID:

 

seg023:0912                   loc_22B42:
seg023:0912 89 46 F8                          mov     word ptr [bp+houseGamePtr], ax
seg023:0915 B8 01 00                          mov     ax, 1

seg023:0915 B0 01                             mov     al, 1
seg023:0918 8A 4E 0A                          mov     cl, byte ptr [bp+houseIndex]
seg023:091B D3 E0                             shl     ax, cl                              ; Shift Logical Left
seg023:091D 89 46 F6                          mov     [bp+houseRevealed], ax
seg023:0920 83 7E 0A 01                       cmp     [bp+houseIndex], 1                  ; Compare Two Operands
seg023:0924 75 0E                             jnz     short notAtreOrWorm                 ; Jump if Not Zero (ZF=0)
seg023:0926 C4 5E 06                          les     bx, [bp+unitGamePtr]                ; Load Full Pointer to ES:xx

seg023:091F C4 5E 06                          les     bx, [bp+unitGamePtr]                ; Load Full Pointer to ES:xx
seg023:0922 83 F9 03                          cmp     cx, 3                               ; Compare Two Operands
seg023:0925 74 0C                             jz      short loc_22B60                     ; Jump if Not Zero (ZF=0)
seg023:0927 83 F9 01                          cmp     cx, 1                               ; Compare Two Operands
seg023:092A 75 0B                             jnz     short notAtreOrWorm  

seg023:0929 26 80 7F 02 19                    cmp     es:[bx+_unitGame.typeIndex], 19h    ; sandworm
seg023:092E 74 04                             jz      short notAtreOrWorm                 ; Jump if Zero (ZF=1)
seg023:0930
seg023:0930                   loc_22B60:                                                  ; Logical Inclusive OR
seg023:0930 83 4E F6 08                       or      [bp+houseRevealed], 8

seg023:0933 83 4E F6 0A                       or      [bp+houseRevealed], 0Ah
seg023:0934
seg023:0934                   notAtreOrWorm:                                              ; CODE XREF: sub_22B24+30j
seg023:0934                                                                               ; sub_22B24+3Aj
seg023:0934 C4 5E 06                          les     bx, [bp+unitGamePtr]                ; Load Full Pointer to ES:xx
seg023:0937 26 8A 47 09                       mov     al, es:[bx+_unitGame.houseRevealed]
seg023:093B B4 00                             mov     ah, 0
seg023:093D 85 46 F6                          test    [bp+houseRevealed], ax              ; Logical Compare
seg023:0940
seg023:0940                   loc_22B70:                                                  ; Jump if Zero (ZF=1)
seg023:0940 74 18                             jz      short loc_22B8A
seg023:0942 C4 5E F8                          les     bx, [bp+houseGamePtr]               ; Load Full Pointer to ES:xx


So I had to work a bit on registers (AL instead AX for example) to gain more bytes room.

0Ah value instead 08 is for the bilateral Atre-Fre and Fre-Atre right behaviours (Logical OR of 1010 binary instead only 1000).

 

Release: v1.07-EU - Address: near 0x0177A0

Search for:
                                          b8 01
00 8a 4e 0a d3 e0 89 46 f6 83 7e 0a 01 75 0e c4
5e 06 26 80 7f 02 19 74 04 83 4e f6 08 c4 5e 06


Replace with:
                                          b0 01
8a 4e 0a d3 e0 89 46 f6 c4 5e 06 83 f9 03 74 0c
83 f9 01 75 0b 26 80 7f 02 19 74 04 83 4e f6 0a

Link to comment
Share on other sites

Translating on asm this modified function (2 of 4):

 

if (unit->o.houseID == g_playerHouseID || (unit->o.houseID == HOUSE_FREMEN && g_playerHouseID == HOUSE_ATREIDES)
   
|| (unit->o.houseID == HOUSE_ATREIDES && g_playerHouseID == HOUSE_FREMEN)) {
        unit->o.seenByHouses = 0xFF;
    } else {
        unit->o.seenByHouses |= houseIDBit;
    }

 

I removed some les (Load Full Pointer to ES:xx) instruction since it isn't really necessary being loaded once at the beginning (seg023:0BBB):

 

seg023:0BBB                   checkHouseID:                                               ; CODE XREF: sub_22B24+2A9j
seg023:0BBB                                                                               ; sub_22B24+2B3j
seg023:0BBB C4 5E 06                          les     bx, [bp+unitGamePtr]                ; Load Full Pointer to ES:xx
seg023:0BBE 26 8A 47 08                       mov     al, es:[bx+_unitGame.houseID]
seg023:0BC2 98                                cbw                                         ; AL -> AX (with sign)
seg023:0BC3 3B 06 38 3A                       cmp     ax, houseHumanID                    ; Compare Two Operands
seg023:0BC7 74 11                             jz      short AtreFre                       ; Jump if Zero (ZF=1)

seg023:0BC7 74 16                             jz      short AtreFre                       ; Jump if Zero (ZF=1)
seg023:0BC9
seg023:0BC9                   loc_22DF9:                                                  ; Load Full Pointer to ES:xx
seg023:0BC9 C4 5E 06                          les     bx, [bp+unitGamePtr]
seg023:0BCC 26 80 7F 08 03                    cmp     es:[bx+_unitGame.houseID], 3        ; Fremen
seg023:0BD1 75 11                             jnz     short loc_22E14                     ; Jump if Not Zero (ZF=0)
seg023:0BD3 83 3E 38 3A 01                    cmp     houseHumanID, 1                     ; Atre
seg023:0BD8 75 0A                             jnz     short loc_22E14                     ; Jump if Not Zero (ZF=0)

seg023:0BC9                   loc_22DF9:              
seg023:0BC9 3C 03                             cmp     al, 3                               ; object Fremen
seg023:0BCB 75 07                             jnz     short checkFriend2                  ; Jump if Not Zero (ZF=0)
seg023:0BCD 83 3E 38 3A 01                    cmp     houseHumanID, 1                     ; human Atre
seg023:0BD2 74 0B                             jz      short AtreFre                       ; Jump if Not Zero (ZF=0)
seg023:0BD4
seg023:0BD4                   checkFriend2:                                               
seg023:0BD4 3C 01                             cmp     al, 1                               ; object Atreides
seg023:0BD6 75 0E                             jnz     short loc_22E14                     ; Jump if Not Zero (ZF=0)
seg023:0BD8 83 3E 38 3A 03                    cmp     houseHumanID, 3                     ; human Fremen
seg023:0BDD 75 07                             jnz     short loc_22E14                     ; Jump if Not Zero (ZF=0)

seg023:0BDA
seg023:0BDA                   AtreFre:                                                    ; CODE XREF: sub_22B24+2D3j
seg023:0BDA C4 5E 06                          les     bx, [bp+unitGamePtr]                ; unit = fremen and human = atre
seg023:0BDD 26 C6 47 09 FF                    mov     es:[bx+_unitGame.houseRevealed], 0FFh
seg023:0BE2 EB 0A                             jmp     short Done                          ; Jump

seg023:0BE4 EB 08                             jmp     short Done                          ; Jump
seg023:0BE4                   ; ---------------------------------------------------------------------------
seg023:0BE4.
seg023:0BE4                   loc_22E14:                                                  ; CODE XREF: sub_22B24+2DDj
seg023:0BE4                                                                               ; sub_22B24+2E4j
seg023:0BE4 C4 5E 06                          les     bx, [bp+unitGamePtr]                ; Load Full Pointer to ES:xx

seg023:0BE6 90                                nop                                         ; No Operation
seg023:0BE7 8A 46 F6                          mov     al, byte ptr [bp+houseRevealed]
seg023:0BEA 26 08 47 09                       or      es:[bx+_unitGame.houseRevealed], al ; Logical Inclusive OR
seg023:0BEE
seg023:0BEE                   Done:                                                       ; CODE XREF: sub_22B24+Fj
seg023:0BEE                                                                               ; sub_22B24+63j ...
seg023:0BEE 5E                                pop     si
seg023:0BEF 8B E5                             mov     sp, bp
seg023:0BF1 5D                                pop     bp

 

Using altready valued AL register we can save more bytes room doing cmp to check Atre-Fre & Fre-Atre exceptions.

No Operation byte it's a filler to doesn't mess the rest of code.

 

 

Release: v1.07-EU - Address: near 0x017A60

Search for:
   11 c4 5e 06 26 80 7f 08 03 75 11 83 3e 38 3a
01 75 0a c4 5e 06 26 c6 47 09 ff eb 0a c4 5e 06


Replace with:
   16 3c 03 75 07 83 3e 38 3a 01 74 0b 3c 01 75
0e 83 3e 38 3a 03 75 08 26 c6 47 09 ff eb 08 90

Link to comment
Share on other sites

Translating on asm this modified function (3 of 4):

 

bool House_AreAllied(uint8 houseID1, uint8 houseID2)
{
    if (houseID1 == HOUSE_INVALID || houseID2 == HOUSE_INVALID) return false;

    if (houseID1 == houseID2) return true;

    if (houseID1 == HOUSE_FREMEN || houseID2 == HOUSE_FREMEN) {
        return (houseID1 == HOUSE_ATREIDES || houseID2 == HOUSE_ATREIDES);
    }

    return (houseID1 != g_playerHouseID && houseID2 != g_playerHouseID);

   
uint8 playerHouse = g_playerHouseID;

    if (playerHouse == HOUSE_FREMEN) playerHouse = HOUSE_ATREIDES;
    if (houseID1 == HOUSE_FREMEN) houseID1 = HOUSE_ATREIDES;
    if (houseID2 == HOUSE_FREMEN) houseID2 = HOUSE_ATREIDES;

    return ((houseID1 == playerHouse) == (houseID2 == playerHouse));

}

 

As suggested dynasty these new checks have a bilateral effect that works on Atre-Fre side and even Fre-Atre side.

I had to replace completely chunk of code:

 

seg023:0F5E                   loc_2318E:
seg023:0F5E 8B 56 06                          mov     dx, [bp+objectHouseID]
seg023:0F61
seg023:0F61                   loc_23191:
seg023:0F61 8B 5E 08                          mov     bx, [bp+HumanHouseID]
seg023:0F64 83 FA FF                          cmp     dx, 0FFFFh                          ; Compare Two Operands
seg023:0F67
seg023:0F67                   loc_23197:                                                  ; Jump if Zero (ZF=1)
seg023:0F67 74 05                             jz      short retFalse
seg023:0F69
seg023:0F69                   loc_23199:                                                  ; Compare Two Operands
seg023:0F69 83 FB FF                          cmp     bx, 0FFFFh
seg023:0F6C 75 04                             jnz     short loc_231A2                     ; no human house?
seg023:0F6E
seg023:0F6E                   retFalse:                                                   ; CODE XREF: isFriendly?:loc_23197j
seg023:0F6E                                                                               ; isFriendly?:notAtreidesj ...
seg023:0F6E 33 C0                             xor     ax, ax                              ; Logical Exclusive OR
seg023:0F70
seg023:0F70                   loc_231A0:                                                  ; CODE XREF: isFriendly?:loc_231D1j
seg023:0F70 EB 31                             jmp     short loc_231D3                     ; Jump

seg023:0F61
seg023:0F61                   SetHumanHouse:
seg023:0F61 A1 38 3A                          mov     ax, houseHumanID

seg023:0F72                   ; ---------------------------------------------------------------------------
seg023:0F72
seg023:0F72                   loc_231A2:                                                  ; CODE XREF: isFriendly?+11j
seg023:0F72 3B D3                             cmp     dx, bx                              ; Compare Two Operands
seg023:0F74
seg023:0F74                   loc_231A4:                                                  ; Jump if Not Zero (ZF=0)
seg023:0F74 75 02                             jnz     short loc_231A8
seg023:0F76 EB 26                             jmp     short retTrue                       ; Jump
seg023:0F78                   ; ---------------------------------------------------------------------------
seg023:0F78
seg023:0F78                   loc_231A8:                                                  ; CODE XREF: isFriendly?:loc_231A4j
seg023:0F78 83 FA 03                          cmp     dx, 3                               ; DX = object
seg023:0F7B 74 05                             jz      short isFremen                      ; Jump if Zero (ZF=1)
seg023:0F7D 83 FB 03                          cmp     bx, 3                               ; BX = human
seg023:0F80
seg023:0F80                   loc_231B0:                                                  ; Jump if Not Zero (ZF=0)
seg023:0F80 75 0E                             jnz     short notFremen
seg023:0F82
seg023:0F82                   isFremen:                                                   ; CODE XREF: isFriendly?+20j
seg023:0F82 83 FA 01                          cmp     dx, 1                               ; Compare Two Operands
seg023:0F85
seg023:0F85                   loc_231B5:                                                  ; Jump if Zero (ZF=1)
seg023:0F85 74 05                             jz      short isAtreides
seg023:0F87
seg023:0F87                   loc_231B7:                                                  ; Compare Two Operands
seg023:0F87 83 FB 01                          cmp     bx, 1
seg023:0F8A
seg023:0F8A                   loc_231BA:                                                  ; if object is Fremen, and Human is Atreides, Ret True
seg023:0F8A 75 02                             jnz     short notAtreides
seg023:0F8C
seg023:0F8C                   isAtreides:                                                 ; CODE XREF: isFriendly?:loc_231B5j
seg023:0F8C EB 10                             jmp     short retTrue                       ; Jump
seg023:0F8E                   ; ---------------------------------------------------------------------------
seg023:0F8E
seg023:0F8E                   notAtreides:                                                ; CODE XREF: isFriendly?:loc_231BAj
seg023:0F8E EB DE                             jmp     short retFalse                      ; Jump
seg023:0F90                   ; ---------------------------------------------------------------------------
seg023:0F90
seg023:0F90                   notFremen:                                                  ; CODE XREF: isFriendly?:loc_231B0j
seg023:0F90 3B 16 2C 3A                       cmp     dx, houseHumanID                    ; Compare Two Operands
seg023:0F94
seg023:0F94                   loc_231C4:                                                  ; Jump if Zero (ZF=1)
seg023:0F94 74 06                             jz      short objectIsHuman
seg023:0F96
seg023:0F96                   loc_231C6:                                                  ; Compare Two Operands
seg023:0F96 3B 1E 2C 3A                       cmp     bx, houseHumanID
seg023:0F9A
seg023:0F9A                   loc_231CA:                                                  ; if object is not human, and house is not human, ret true
seg023:0F9A 75 02                             jnz     short retTrue
seg023:0F9C
seg023:0F9C                   objectIsHuman:                                              ; CODE XREF: isFriendly?:loc_231C4j
seg023:0F9C EB D0                             jmp     short retFalse                      ; Jump
seg023:0F9E                   ; ---------------------------------------------------------------------------
seg023:0F9E
seg023:0F9E                   retTrue:                                                    ; CODE XREF: isFriendly?+1Bj
seg023:0F9E                                                                               ; isFriendly?:isAtreidesj ...
seg023:0F9E B8 01 00                          mov     ax, 1
seg023:0FA1
seg023:0FA1                   loc_231D1:                                                  ; Jump
seg023:0FA1 EB CD                             jmp     short loc_231A0

seg023:0F72
seg023:0F72                   SetFriend1:                                                 ; CODE XREF: isFriendly?+11j
seg023:0F72 3D 03 00                          cmp     ax, 3                               ; Compare Two Operands
seg023:0F7B 75 03                             jnz     short SetFriend2                    ; Jump if Not Zero (ZF=0)
seg023:0F9E B8 01 00                          mov     ax, 1
seg023:0F72
seg023:0F72                   SetFriend2:
seg023:0F78 83 FA 03                          cmp     dx, 3                               ; DX = objectHouseID1
seg023:0F7B 75 03                             jnz     short SetFriend3                    ; Jump if Not Zero (ZF=0)
seg023:0F9E BA 01 00                          mov     dx, 1
seg023:0F72
seg023:0F72                   SetFriend3:   
seg023:0F78 83 FB 03                          cmp     bx, 3                               ; BX = objectHouseID2
seg023:0F7B 75 03                             jnz     short CheckFriend1                  ; Jump if Not Zero (ZF=0)
seg023:0F9E BB 01 00                          mov     bx, 1
seg023:0F72
seg023:0F72                   CheckFriend1:
seg023:0F72 3B C2                             cmp     ax, dx                              ; Compare Two Operands
seg023:0F7B 75 05                             jnz     short Res1False                     ; Jump if Not Zero (ZF=0)
seg023:0F9E BA 01 00                          mov     dx, 1
seg023:0F70 EB 03                             jmp     short CheckFriend2                  ; Jump
seg023:0F72
seg023:0F72                   Res1False:
seg023:0F9E BA 02 00                          mov     dx, 2
seg023:0F72
seg023:0F72                   CheckFriend2:
seg023:0F72 3B C3                             cmp     ax, bx                              ; Compare Two Operands
seg023:0F7B 75 05                             jnz     short Res2False                     ; Jump if Not Zero (ZF=0)
seg023:0F9E BB 01 00                          mov     bx, 1
seg023:0F70 EB 03                             jmp     short LastCheck                     ; Jump
seg023:0F72
seg023:0F72                   Res2False:
seg023:0F9E BB 02 00                          mov     bx, 2
seg023:0F72
seg023:0F72                   LastCheck:                                                  ; CODE XREF: isFriendly?+11j
seg023:0F72 3B D3                             cmp     dx, bx                              ; Compare Two Operands
seg023:0F74 75 05                             jnz     short retFalse
seg023:0F9E
seg023:0F9E                   retTrue:                                                    ; CODE XREF: isFriendly?+1Bj
seg023:0F9E                                                                               ; isFriendly?:isAtreidesj ...
seg023:0F9E B8 01 00                          mov     ax, 1
seg023:0FA1 EB 02                             jmp     short Done
seg023:0F6E
seg023:0F6E                   retFalse:                                                   ; CODE XREF: isFriendly?:loc_23197j
seg023:0F6E                                                                               ; isFriendly?:notAtreidesj ...
seg023:0F6E 33 C0                             xor     ax, ax    

seg023:0FA3                   ; ---------------------------------------------------------------------------
seg023:0FA3
seg023:0FA3                   loc_231D3:                                                  ; CODE XREF: isFriendly?:loc_231A0j
seg023:0FA3 5D                                pop     bp

 

Using AX register I valued global houseHumanID variable and I have done all requested checks.

LastCheck tests on DX & BX registers that I valued to 1 if true or 2 if false. ;) You might choose other values to represent true or false, the important thing is that they are equal to represent true or false values.

 

 

Release: v1.07-EU - Address: near 0x017DF0

Search for:
                                       83 fa ff
74 05 83 fb ff 75 04 33 c0 eb 31 3b d3 75 02 eb
26 83 fa 03 74 05 83 fb 03 75 0e 83 fa 01 74 05
83 fb 01 75 02 eb 10 eb de 3b 16 38 3a 74 06 3b
1e 38 3a 75 02 eb d0 b8 01 00 eb cd


Replace with:
                                       a1 38 3a
3d 03 00 75 03 b8 01 00 83 fa 03 75 03 ba 01 00
83 fb 03 75 03 bb 01 00 3b c2 75 05 ba 01 00 eb
03 ba 02 00 3b c3 75 05 bb 01 00 eb 03 bb 02 00
3b d3 75 05 b8 01 00 eb 03 33 c0 90

Link to comment
Share on other sites

Translating on asm this modified function (4 of 4):

 

s = Structure_Get_ByPackedTile(packed);
    if (s != NULL) {
        s->o.seenByHouses |= 1 << houseID;
        if (s->o.houseID == HOUSE_ATREIDES
|| s->o.houseID == HOUSE_FREMEN) s->o.seenByHouses |= (1 << HOUSE_FREMEN)+(1 << HOUSE_ATREIDES);
    }

 

I thought to remove notNull check to free room bytes, apparently it doesn't have any side effects, but if we do some more tests it's better:

 

ovr194:131E 89 56 FA                          mov     word ptr [bp+buildingGamePtr+2], dx
ovr194:1321 89 46 F8                          mov     word ptr [bp+buildingGamePtr], ax
ovr194:1324 8B 46 F8                          mov     ax, word ptr [bp+buildingGamePtr]
ovr194:1327 0B 46 FA                          or      ax, word ptr [bp+buildingGamePtr+2] ; Logical Inclusive OR
ovr194:132A 74 1C                             jz      short loc_57858                     ; Jump if Zero (ZF=1)

ovr194:132C C4 5E F8                          les     bx, [bp+buildingGamePtr]            ; Load Full Pointer to ES:xx
ovr194:132F B0 01                             mov     al, 1
ovr194:1331 8A 4E 08                          mov     cl, byte ptr [bp+HouseID]
ovr194:1334 D2 E0                             shl     al, cl                              ; 1 << houseID
ovr194:1336 26 08 47 09                       or      es:[bx+_buildingGame.housesRevealed], al ; Logical Inclusive OR

ovr194:1335 83 7E 08 03                       cmp     [bp+HouseID], 3                     ; Fremen
ovr194:1339 74 08                             jz      short AtreOrFrem                    ; Jump if Zero (ZF=1)

ovr194:133A 83 7E 08 01                       cmp     [bp+HouseID], 1                     ; Atredies
ovr194:133E 75 08                             jnz     short loc_57858                     ; Jump if Not Zero (ZF=0)
ovr194:1340 C4 5E F8                          les     bx, [bp+buildingGamePtr]            ; Load Full Pointer to ES:xx

ovr194:133F 75 07                             jnz     short notAtreOrFre                  ; Jump if Not Zero (ZF=0)
ovr194:1341 90 90                             nop                                         ; No Operations

ovr194:1343
ovr194:1343                   AtreOrFrem:                                                 ; CODE XREF: mapGamePieceReveal+C1j

ovr194:1343 26 80 4F 09 08                    or      es:[bx+_buildingGame.housesRevealed], 8 ; Logical Inclusive OR

ovr194:1343 26 80 4F 09 0A                    or      es:[bx+_buildingGame.housesRevealed], 0Ah ; Logical Inclusive OR
ovr194:1348
ovr194:1348                   loc_57858:                                                  ; CODE XREF: mapGamePieceReveal+C1j
ovr194:1348       

 

Also in this case there was the possibility to use valued CL register to do cmp to save two room bytes, but added to two 90 nopED, it didn't give the 5 bytes useful to leave notNull check at the beginning that I removed.

Again the Logical OR is made on 0Ah value instead 08 to use bilateral Atre-Fre & Fre-Atre mask (1010 binary instead only 1000).

 

 

Release: v1.07-EU - Address: near 0x04CC20

Search for:
                     0b 46 fa 74 1c c4 5e f8 b0
01 8a 4e 08 d2 e0 26 08 47 09 83 7e 08 01 75 08
c4 5e f8 26 80 4f 09 08


Replace with:
                     c4 5e f8 b0 01 8a 4e 08 d2
e0 26 08 47 09 83 7e 08 03 74 08 83 7e 08 01 75
07 90 90 26 80 4f 09 0a

Link to comment
Share on other sites

Doing exactly all these steps we can have definitively a bilateral alliance between 2 Houses.

I make examples about Atre-Fre & Fre-Atre alliance, but it's possible make what we would like just changing NUMBERS on compare (cmp) checks on HouseIDs. ;)

I invite all of you to do some other tests because maybe I did something wrong, so we can fix it if necessary.

 

 

MrFlibble, moreover I successfully changed Sardaukar Trooper "trike explosion" to "foot units dead", so no more side effect about run over them and cause damage on vehicles (assuming the presence of explosive equipment, but it's not like Dune Background).

Later I'll post workaround about it. :)

 

Including "Sabouter Tile Detonation Fix" (previous thread page), we should have fixed the major issues about modding, right?

What else?

Link to comment
Share on other sites

drnovice, once again, excellent work! Or should I say, super awesome excellent work, as you're dealing with this issues very professionally. Nice to see such dedicated approach :D

Doing exactly all these steps we can have definitively a bilateral alliance between 2 Houses.

I make examples about Atre-Fre & Fre-Atre alliance, but it's possible make what we would like just changing NUMBERS on compare (cmp) checks on HouseIDs. ;)

I invite all of you to do some other tests because maybe I did something wrong, so we can fix it if necessary.

Once again I don't have the time to carefully look through all your code analysis, but am I right that what you have posted means we can have an alliance that works both ways within the same EXE? No more "allies" who just stand there and do nothing, right?

 

MrFlibble, moreover I successfully changed Sardaukar Trooper "trike explosion" to "foot units dead", so no more side effect about run over them and cause damage on vehicles (assuming the presence of explosive equipment, but it's not like Dune Background).

Later I'll post workaround about it. :)

Great, thanks a lot!

There are lots of deviations from the original books already though, and the Sardaukar are described as fanatically loyal to the Emperor, so perhaps the explosive effect could be retained, albeit with a different animation?

Including "Sabouter Tile Detonation Fix" (previous thread page), we should have fixed the major issues about modding, right?

What else?

You've fixed the saboteurs' accidental detonation? Did I miss that? If you have, that's uber awesome, no less ^_^

Once again, sorry I don't have the time to properly study and appreciate all your work right now, but at any rate, what you're doing is an immeasurable contribution to the Dune II modding community. Thanks!

Link to comment
Share on other sites

Once again I don't have the time to carefully look through all your code analysis, but am I right that what you have posted means we can have an alliance that works both ways within the same EXE? No more "allies" who just stand there and do nothing, right?

Exactly. No problem if you don't have much time, you can take a look anytime.

 

Great, thanks a lot!

There are lots of deviations from the original books already though, and the Sardaukar are described as fanatically loyal to the Emperor, so perhaps the explosive effect could be retained, albeit with a different animation?

Maybe but you should change somewhere even on EXE file, because UNIT.EMC calls some sub-rutines marked on EXE, that regards explosion animation dependig type of units and what it calls.

I just decompile original UNIT.EMC within DUNE.PAK, changing the calls of the sub-rutines (using DST_1.2):

 

[Trooper]

PushReg             0

Push                10

Evaluate            Equal

IfNotGoto           l1617

Execute             StopMove

Push                30

Push                4

Push                0

Execute             RandomNumber

AddSP               2

PushOp              0

Evaluate            Add

Execute             PlaySFX

AddSP               1

PushReg             1

Push                255

Evaluate            NotEqual

IfNotGoto           l1616

Execute             sub_279AB

l1616:

Execute             Destroy

l1617:

PushOp              1

Goto                l1363

Pop                  (Return)

 

I took sub_279AB (manages foot units death animation) and put it on Raider Trike:

 

[RaiderTrike]

PushReg             0

Push                10

Evaluate            Equal

IfNotGoto           l2102

Execute             StopMove

PushReg             1

Push                255

Evaluate            NotEqual

IfNotGoto           l2100

Push                10

Execute             sub_26A69

Execute             sub_279AB

AddSP               1

l2100:

Execute             DestroyedMessage

Execute             Destroy

l2102:

PushOp              1

Goto                l1363

Pop                  (Return)

 

Then recompile the TXT and repack DUNE.PAK with new UNIT.EMC. You can research these "sub_xxxxx" on segra db.

If you don't want alter DUNE.PAK to keep original Dune 2 gameplaying, you can pack UNIT.EMC into your EXPAND.PAK archive and then define where DUNEX.EXE takes it with Nyerguds' editor on "Files" section. :)

 

You've fixed the saboteurs' accidental detonation? Did I miss that? If you have, that's uber awesome, no less ^_^

Once again, sorry I don't have the time to properly study and appreciate all your work right now, but at any rate, what you're doing is an immeasurable contribution to the Dune II modding community. Thanks!

Yes, take a look on previous page. ;)

  • Upvote 1
Link to comment
Share on other sites

There are lots of deviations from the original books already though, and the Sardaukar are described as fanatically loyal to the Emperor, so perhaps the explosive effect could be retained, albeit with a different animation?
I thought I already fixed this in the "Dune II editor with 1.07 support" thread. It was a single byte change, so no recompiling really required.
You've fixed the saboteurs' accidental detonation? Did I miss that? If you have, that's uber awesome, no less ^_^
Yes, take a look on previous page. ;)
As I described elsewhere, the detonation depends on both terrain and game speed. On normal speed, the saboteur will almost always detonate when reaching the destination. As far as I can tell, this was the intended behaviour. I don't know about the Amiga version though.

I think your change will make it impossible for to saboteur to explode on units. For Dune II, you probably have to pick between saboteurs that are easy to control but can't explode on units, or saboteurs that will detonate wherever they stop.

BTW, consider changing the call to Unit_Free below to Unit_Remove. This has to do with the "trace" that saboteurs leave behind after "random" detonation.

You can see both issues here:

https://github.com/OpenDUNE/OpenDUNE/issues/138

  • Upvote 1
Link to comment
Share on other sites

I thought I already fixed this in the "Dune II editor with 1.07 support" thread. It was a single byte change, so no recompiling really required.

Sorry I can't find it in forum threads. Can you post here the link?

 

As I described elsewhere, the detonation depends on both terrain and game speed. On normal speed, the saboteur will almost always detonate when reaching the destination. As far as I can tell, this was the intended behaviour. I don't know about the Amiga version though.

I think your change will make it impossible for to saboteur to explode on units. For Dune II, you probably have to pick between saboteurs that are easy to control but can't explode on units, or saboteurs that will detonate wherever they stop.

Yes it's a 'de gustibus' argument.

Do you prefer to detonate always your saboter when you must move him somewhere even if you haven't yet a real target?

I prefer detonate him when I looking for a main enemy structure.

Then you could always put saboteur below a Tank and wait that enemy shoot against it, saboteur will explode after a while. :)

 

BTW, consider changing the call to Unit_Free below to Unit_Remove. This has to do with the "trace" that saboteurs leave behind after "random" detonation.

You can see both issues here:

https://github.com/O...DUNE/issues/138

You're right, there is another matter about exploded tiles that can't be used anyway after it.

I was considering whether it could be an advantage to those who detonates the saboteur, or if it is really an annoying thing.

Link to comment
Share on other sites

As I described elsewhere, the detonation depends on both terrain and game speed. On normal speed, the saboteur will almost always detonate when reaching the destination. As far as I can tell, this was the intended behaviour. I don't know about the Amiga version though.

Hmm, I never considered the possibility of this having been intended behaviour really. Then again, it is my understanding that at some point in the development, the player wasn't supposed to control the saboteur anyway, as it would belong to the allied Mercenary faction (as is the case in the SMD version of the game).

Link to comment
Share on other sites

If for SMD you mean Sega Mega Drive, I don't know this version, but I remember perfectly that Saboteur in Amiga Version doesn't detonate on free tiles anyway.

Since that default Sabouter command is "Sabotage" and CPU doesn't change this, of course "Move" command it's suitable to Human player, and on PC original version it's very impratical.

Link to comment
Share on other sites

Sorry I can't find it in forum threads. Can you post here the link?
dune2x sardaukar trooper explosion change:

http://forum.dune2k.com/topic/19114-dune-2-dune-ii-editor-with-107-support/?p=377807

 

@dynasty: do you like it? ;)
Looks better to use than saboteurs that explode all the time. They might have reworked the detonation behaviour in the Amiga version.

Still, I couldn't resolve the weird Tile_GetDistance call in any other way.

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...