Yes, it seems to crash on yellow's turn. If I remove the ship movement assignment there is no crash. However it's strange. Ship movement assignment happens after spellcasting but when the game does crash yellow doesn't cast a spell. It feels like red messes something up which causes yellows turn to fail.
Disabling the instant disembark on movement feature still results in the crash so the bug is likely in assigning the movement. Unfortunately I don't have enough time right now to try fixing that, and won't be at home tomorrow so it'll have to wait until Monday.
In the unlikely case someone can do it faster, this is the current source code for that part :
mov word ptr [bp-16h],0 ; Ships in stack
mov word ptr [bp-18h],1 ; Ships only in stack
mov word ptr [bp-1Ah],0 ; Settlers in stack
mov word ptr [bp-32h], 0 ; Going unit in stack
les bx, [9E8Ch]
add bx, si
mov al, es:[bx]
cbw
mov [bp-12h], ax ; Stack # of units
les bx, [9E9Ch]
add bx, si
mov al, es:[bx]
cbw
mov [bp-20h], ax ; Stack Pos X
les bx, [9E98h]
add bx, si
mov al, es:[bx]
cbw
mov [bp-22h], ax ; Stack pos Y
mov bx, di
shl bx, 1
mov ax, word ptr [bx-6F4Ah]
mov bx, di
shl bx, 1
mov bx, word ptr [bx-6F2Ch]
mov cx, [bp-22h]
mov dx, [bp-20h]
call overlanddistance
cmp ax,[bp-8]
jge nextshippoint5
mov [bp-8],ax
mov bx, di
shl bx, 1
mov ax, [bx-6F2Ch]
mov [bp-4], ax ; Target Shippoint X
mov bx, di
shl bx, 1
mov ax, [bx-6F4Ah]
mov [bp-6], ax ; Target Shippoint Y
mov [bp-2Ch], di ; Target Shippoint ID
nextshippoint5:
inc di
jmp nextshippoint6
shippointsdone2:
cmp word ptr [bp-8], 2710h
jl sendtoshippoint
call ShipAttackTarget ; If ship is not required, attack targets or perform idle movement
jmp sendto2
sendtoshippoint:
mov bx, [bp-2Ch] ; Target Shippoint ID
shl bx, 1
lea ax, [bp-54h]
add bx, ax
mov ax, [bx]
mov [bp-2Eh], ax ; Capacity needed for this shippoint
sendto2:
mov word ptr [bp-28h], 0
sendloop1:
mov ax, [bp-28h]
cmp ax, [bp-12h] ; Stack total units
jge donesending
cmp word ptr [bp-2Eh], 0
jle donesending
mov bx, si
mov cl, 2
shl bx, cl
les bx, [bx-62B4h]
mov ax, [bp-28h]
shl ax, 1
add bx, ax
mov ax, es:[bx]
mov [bp-2Ah], ax
mov ax, [bp-2Ah]
call getoverland
call getunittype
mov al, [bx+1B0h]
cbw
inc ax
sub [bp-2Eh], ax ; Subtract transport capacity from shippoint required capacity
donesending:
mov bx, [bp-2Ch]
shl bx, 1
lea ax, [bp-54h]
add bx, ax
mov ax, [bp-2Eh]
mov [bx], ax ; Store required capacity for shippoint
cmp ax,0
jg nextstack2
mov bx, [bp-2Ch]
shl bx, 1
mov word ptr [bx-6F68h], 0FFFFh ; Clear shippoint if no more needed
jmp nextstack2
;=========================================
; Stack is transporting units
;=========================================
stackistransport:
cmp word prt [bp-32h],0 ; A unit already had a going order. do not move stack until it leaves (probably impossible situation in the current system)
jnz nextstack2
;=========================================
; Check all adjacent tiles for land
;=========================================
call tileaction
cmp word ptr [bp-0Ch],0FFFFh
jz movetotargetcontinent
;=========================================
; Disembark if able
;=========================================
mov bx, si
mov cl, 2
shl bx, cl
les bx, [bx-62B4h]
mov ax, di
shl ax, 1
add bx, ax
mov ax, es:[bx]
mov [bp-2Ah],ax
call getoverland
call getunittype
cmp byte ptr [bx+1B0h], 0
jle x1x1
push di
push si
push [bp-2Ah]
push cs
call RemoveFromStack
add sp, 6
jmp nextunit7
x1x1:
push di
push si
push [bp-0Eh]
push [bp-0Ch]
push [bp-2Ah]
push cs
call AssignMovement
add sp, 0Ah
nextunit7:
inc di
jmp nextunit8
;==================================================
; Not near a destination continent, move there!
;==================================================
movetotargetcontinent:
cmp word ptr [bp-1Ah],1
jz issettler2
do7050instead:
mov word ptr [bp-1Ah],0 ; No settling continent, pretend we have no settler
call get7050
jmp movetothiscontinent
issettler2:
call get7054
or ax,ax
jz do7050instead
movetothiscontinent:
or ax,ax ; no 7050 continent, nowhere to go!!!
jz nextstack2
; Find
mov ax,[bp-22h]
mov cx,[bp-20h]
mov [bp-26h],ax ; Out current position
mov [bp-24h],cx
mov word ptr [bp-1Ch],0FFFFh ; best unload position X (land)
mov word ptr [bp-1Eh],0FFFFh ; best unload position Y
mov word ptr [bp-2Ah],0FFFFh ; best unload position X (sea)
mov word ptr [bp-2Ch],0FFFFh ; best unload position Y
mov word ptr [bp-14h],08888h ; best unload position priority
mov word ptr [bp-22h],0
nexty2:
mov word ptr [bp-20h],0
nextx2:
mov ax,[bp-22h]
mov cx,[bp-20h]
call getstackstrength
or ax,ax
jnz nexttile3
call tileaction
cmp word ptr [bp-0Ch],0FFFFh
jz nexttile3
; We found a tile where we can disembark
cmp word ptr [bp-1Ah],1
jz issettler5
mov ax,[bp-26h] ; Closest to our current location is best
mov bx,[bp-24h]
mov cx,[bp-0Eh]
mov dx,[bp-0Ch]
jmp distprio
pop ax
call getoverland
; Store disembark position here! (bp-2A, bp-2C)
mov ax,[bp-2Ch]
sub ax,[bp-1Eh]
inc ax
sal ax,2
mov cx,[bp-2Ah]
sub cx,[bp-1Ch]
inc cx
add cx,ax
mov byte ptr es:[bx+1Dh],cl
inc word ptr [bp-28h]
jmp sendloop4
donesending3:
nextstack2:
inc si
jmp nextstack
; in : bp-20h, bp-22h position
;===============================================================================
; tileacion : find if there is a tile we want to disembark to from our position
;===============================================================================
tileaction:
push di
mov word ptr [bp-0Ch],0FFFFh ; best unload position X
mov word ptr [bp-0Eh],0FFFFh ; best unload position Y
mov word ptr [bp-10h],0 ; best unload position priority
mov word ptr [bp-4],0 ; X modifier
nextx1:
mov word ptr [bp-6],0 ; Y modifier
nexty1:
call loadposition
; Position must be valid. Relevant land can't be on the edge of the map anyway.
cmp ax,28h
jge notland1
cmp ax,0
jl notland1
cmp cx,0
jl notland1
cmp cx,3Ch
jge notland1
call getcontinentID
or al,al ; It's a sea tile...
jz notland1
mov [bp-8],al ; Continent ID
mov word ptr [bp-0Ah],0
call loadposition
call tileoccupied
or ax,ax ; Tile is empty
jz attackok
;jmp notland1 ;******** testing
; Tile has at least one unit on it!
call loadposition
call getstackstrength
mov [bp-0Ah],ax
test dx,08000h ; Not an enemy : can't attack
jz notland1
add ax,60
sal ax,1; We're leaving ships behind and miss chance to group with other unloaded troops so only do this if we are quite a lot stronger.
cmp word ptr [bp-1Ah],1 ; Don't attack with settler stacks
jz notland1
push ax
mov ax,[bp-22h]
mov cx,[bp-20h]
call getstackstrength
pop cx
cmp ax,cx
jle notland1 ; We are too weak to attack that
;=========================================================================
; Found a land tile we could move to, check if continent is appropriate
;=========================================================================
attackok:
cmp word ptr [bp-1Ah],1
jz settlerstack
;=========================================================================
; Not settler stack
;=========================================================================
; We don't care about continents type 5 and 6 (no targets)
call getcontinenttype
cmp al,5
jge notland1
; Is main action continent? If yes, top priority!
call get7050
cmp al,[bp-8]
jnz notmaincontinent1
call loadposition
mov dx,5000 ; Priority
add dx,[bp-0Ah] ; add strength of enemy : always prefer to attack when able
jmp foundpossiblelocation
notmaincontinent1:
; Can't have own or allied city on this continent unless it was the main action continent.
; this is to prevent getting off the ship where we got on.
xor di,di
nextcity:
call getcity
mov al, es:[bx+11h]
cbw
cmp ax, [bp+8] ; Plane
jnz nextcity2
mov al,es:[bx+10h]
mov cl,es:[bx+0Fh]
xor ah,ah
xor ch,ch
call getcontinentID
cmp al,[bp-8] ; Same continent?
jnz nextcity2
call getcity
mov al, es:[bx+12h]
cbw
cmp ax, [bp+6]
jz notland1 ; Own city on continent, we don't want to disembark here
push ax
mov ax, [bp+6]
mov dx, 4C8h
imul dx
pop bx
add bx, ax
cmp byte ptr [bx-5FDEh], 2
jz notland1 ; Allied city!
nextcity2:
inc di
cmp di,[0BD94h]
jl nextcity
call loadposition
mov dx,10 ; Priority, less than on 7050 continent
add dx,[bp-0Ah] ; add strength of enemy : always prefer to attack when able
jmp foundpossiblelocation
or ax,ax ; If 7054 continent doesn't exist then anything goes
jz settlethis
cmp al,[bp-8]
jz settlethis
jmp notland1
settlethis:
call loadposition
mov dx,10 ; Priority doesn't really matter here, there is only one case...
foundpossiblelocation:
push ax
push bx
push cx
push dx
call cityexist
pop dx
or ax,ax
jz nocity2
add dx,600 ; 3 tiles of extra distance worth of priority for enemy cities.
; This much should be enough to hit them with nearby transports, while
; low enough to not make it impossible to manipulate the transport by
; moving garrisons in and out effectively.
nocity2:
pop cx
pop bx
pop ax
inc word ptr [bp-6]
cmp word ptr [bp-6],2
jle nexty1
inc word ptr [bp-4]
cmp word ptr [bp-4],2
jle nextx1
; return selected tile if any in bp-0C, bp-0E
pop di
retn
tileoccupied:
; Return 1 if tile has a unit
mov dl,cl ; X
mov dh,al ; Y
xor cx,cx
nextxx:
cmp cx,[0BD92h]
jge allchecked1
mov ax,cx
push dx
call getoverland
pop dx
cmp dx,es:[bx]
jnz notthere
mov al,es:[bx+2]
cmp al,[bp+8]
jnz notthere
haslair:
mov ax,1
retn
notthere:
inc cx
jmp nextxx
allchecked1:
call loadposition
call getstackstrength
or ax,ax
jnz haslair
retn
cityexist:
xor di,di
nextcity4:
call getcity
mov al, es:[bx+11h]
cbw
cmp ax, [bp+8] ; Plane
jnz nextcity5
call loadposition
cmp al,es:[bx+10h]
jnz nextcity5
cmp cl,es:[bx+0Fh]
jnz nextcity5
mov bl,es:[bx+12h]
xor bh,bh
sal bx,1
mov ax,4C8h
imul word ptr [bp+6] ; Wizard
add bx,ax
cmp word ptr [bx-5C98h],2 ; Must be hostile towards that wizard
jl nextcity5
mov ax,1
retn
nextcity5:
inc di
cmp di,[0BD94h]
jl nextcity4
xor ax,ax
retn
Monsters are still disappearing somewhere after "rampaging through a city and then disappearing into the wilderness." Here is early game save game where monster attacks the next turn if needed for debugging.
That works as intended now - they have to otherwise they'd stay in your city and cause problems. If the monsters win, they aren't sent back to the tile they came from.
After reading through the entire ship code I found no bugs that can be related. However it is definitely there - if I change the code to pretend none of the AI's stacks have any ships in them so nothing is actually done, the crash disappears. I'll have to keep looking...
Found the problem. When the stack contained a unit that was already removed from the stack and marked "absent" the game crashed because it still tried to set the new disembark tactic on it.
Thanks. I'm not sure if this is a bug but in that save2.zip save file there is by the way also a monster stack stuck one square above the town of "Fell Gorge", besides a tower, with nowhere to go except the sea or that red city where it doesn't go. However, couple of turns later red took care of the problem by attacking that stack.
Those are blocked from movement. While neutrals are allowed to move through and attack stacks of units on their way to a destination, cities, lairs, towers, nodes still count as impassable tiles. However, neutrals do not check for their target to be reachable when picking one - they assume any destination on the same continent should be reachable (or any destination is ok if the stack is capable of moving on water).
Overall, this is rare enough to not worth trying to fix - and even then it's very possible to have the tile be sealed by 2 lairs instead of a lair and a city.
Having two different "targets" on the same tile is a bad idea, if someone moves on the tile they'd attack which one? Which one gets displayed? How does the AI target the tile? How do we prevent the stack from attacking the lair? Even if we manage to come up with a solution, it's too much work to implement from something like this.