diff --git a/programs/games/Dungeons/AKODE/AKODE.inc b/programs/games/Dungeons/AKODE/AKODE.inc
new file mode 100644
index 0000000000..45c97b86fd
--- /dev/null
+++ b/programs/games/Dungeons/AKODE/AKODE.inc
@@ -0,0 +1,3428 @@
+; Copyright (C) 2014 Anton_K
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see .
+
+include '../../../develop/libraries/libs-dev/libio/libio.inc'
+include '../../../develop/libraries/libs-dev/libimg/libimg.inc'
+
+include 'datadef.inc'
+
+; ============================================================================ ;
+; < eax = 0 - fail ;
+; ============================================================================ ;
+proc akode.init uses ebx ecx edx, plane_width, plane_height, fov, block_base_size, block_height
+ bsr ecx, [block_base_size]
+ xor eax, eax
+ inc eax
+ shl eax, cl
+ mov [akode_data.BlockWidthPowerOf2], ecx
+ mov [akode_data.BlockSize.Width], eax
+
+ mov eax, [block_height]
+ mov [akode_data.BlockSize.Height], eax
+
+ shr eax, 1
+ mov [akode_data.Camera.Position.Z], eax
+
+ xor eax, eax
+ mov [akode_data.Camera.Position.X], eax
+ mov [akode_data.Camera.Position.Y], eax
+ mov [akode_data.Camera.Direction], eax
+ mov [akode_data.OptimizedGetImage], eax
+ dec eax
+ mov [akode_data.MovementDirection], eax
+ mov [akode_data.TurningDirection], eax
+ inc eax
+ stdcall akode.set_shading, eax, eax
+ test eax, eax
+ jz .exit
+
+ mov edx, [plane_width]
+
+ mov ecx, edx
+ shl ecx, 2
+ mcall 68, 12 ; alloc width * 4 bytes
+
+ test eax, eax
+ jz .exit
+
+ mov [akode_data.DepthBufferPtr], eax
+
+ mov eax, [plane_height]
+ mov ecx, eax
+ shl ecx, 2
+ imul ecx, edx ; ecx = height * 4 * width
+
+ mov [akode_data.ProjectionPlane.Size.Height], eax
+ mov [akode_data.ProjectionPlane.Size.Width], edx
+ mov [akode_data.ImageBufferSize], ecx
+
+ mov ebx, eax
+ or ebx, edx
+ test ebx, 0Fh
+ jnz @f
+ inc [akode_data.OptimizedGetImage]
+@@:
+
+ shr eax, 1
+ mov [akode_data.ProjectionPlane.MidY], eax ; MidY = height / 2
+
+ mcall 68, 12 ; alloc ecx bytes
+
+ test eax, eax
+ jz .exit
+
+ mov [akode_data.ImageBufferPtr], eax
+ DEBUGF DEBUG_FINE, 'akode_data.ImageBufferPtr: %x\n', eax:8
+ DEBUGF DEBUG_FINE, 'akode_data.OptimizedGetImage: %u\n', [akode_data.OptimizedGetImage]
+
+ mov ecx, [fov]
+ mov ebx, edx
+ imul eax, edx, 360
+ xor edx, edx
+ div ecx ; eax = width * 360 / fov
+
+ mov [akode_data.Camera.FieldOfView], ecx
+
+ mov [akode_data.Angle360], eax
+
+ stdcall akode._.calc_tables
+
+ test eax, eax
+ jz .exit
+
+ shr ebx, 1 ; ebx = width / 2
+ push ebx
+ shl ebx, 5 ; ebx * 32
+ mov eax, [akode_data.TrigonometricTablePtr]
+
+ fild dword [esp] ; width / 2
+ fdiv qword [eax + ebx + 16] ; tan (fov / 2)
+ fistp dword [esp]
+
+ pop eax
+ mov [akode_data.CameraToPlaneDistance], eax
+ mov ebx, eax
+
+ mul [block_height]
+ mov [akode_data.WallHeightDividend], eax
+
+ mov eax, [block_height]
+ sub eax, [akode_data.Camera.Position.Z]
+ mul ebx
+ mov [akode_data.CeilingDistanceDividend], eax
+
+ imul ebx, [akode_data.Camera.Position.Z]
+ mov [akode_data.FloorDistanceDividend], ebx
+
+ DEBUGF DEBUG_FINE, 'akode_data.CameraToPlaneDistance: %u\n', [akode_data.CameraToPlaneDistance]
+
+ stdcall akode.set_movement_speed, [akode_data.BlockSize.Width], 90
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.cleanup uses eax ebx ecx edx
+ stdcall akode.unload_current_level
+
+ xor edx, edx
+ mov ebx, 13
+
+ mov ecx, [akode_data.ImageBufferPtr]
+ test ecx, ecx
+ jz @f
+ mcall 68 ; free
+ mov [akode_data.ImageBufferPtr], edx
+@@:
+ mov ecx, [akode_data.DepthBufferPtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [akode_data.DepthBufferPtr], edx
+@@:
+ mov ecx, [akode_data.TrigonometricTablePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [akode_data.TrigonometricTablePtr], edx
+@@:
+ mov ecx, [akode_data.BlockWidthTanTablePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [akode_data.BlockWidthTanTablePtr], edx
+@@:
+ mov ecx, [akode_data.ShadingTablePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [akode_data.ShadingTablePtr], edx
+@@:
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > level_load_callback = proc callback AKODE_LEVEL_LOAD, result ;
+; > action_callback = proc callback AKODE_ACTION, cell x, cell y, result ;
+; ============================================================================ ;
+proc akode.set_callbacks level_load_callback, action_callback
+ m2m [akode_data.LevelLoadCallback], [level_load_callback]
+ m2m [akode_data.ActionCallback], [action_callback]
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 0 - failed to load some textures or create shading table ;
+; ============================================================================ ;
+proc akode.load_level uses ebx ecx edx esi edi, level_ptr
+ locals
+ load_texture_results dd 1
+ endl
+
+ stdcall akode.unload_current_level
+
+ mov ebx, akode_data.CurrentLevel
+
+ cld
+ mov esi, [level_ptr]
+ mov edi, ebx
+ mov ecx, sizeof.akode.LevelHeader
+ rep movsb
+
+ mov [akode_data.CurrentLevelGridPtr], esi
+
+ mov eax, [ebx + akode.LevelHeader.InitCallback]
+ test eax, eax
+ jz @f
+ stdcall eax
+
+@@:
+ mov edx, [akode_data.LevelLoadCallback]
+ test edx, edx
+ jz @f
+ stdcall edx, AKODE_LEVEL_LOAD.START, eax
+
+@@:
+ mov ecx, [ebx + akode.LevelHeader.Size.Width]
+ imul ecx, [ebx + akode.LevelHeader.Size.Height]
+
+.load_textures_loop:
+ mov edi, [esi]
+ add esi, 4
+
+ test edi, edi
+ jz .skip
+
+ push ecx
+
+ mov ecx, 6 * 2 ; 6 combined textures per cell
+
+.load_cell_textures_loop:
+ mov eax, [edi]
+ add edi, 4
+
+ test eax, eax
+ jz @f
+
+ stdcall akode.load_texture, eax
+
+ test eax, eax
+ jnz @f
+ mov [load_texture_results], eax
+
+@@:
+ sub ecx, 1
+ jnz .load_cell_textures_loop
+
+ pop ecx
+
+.skip:
+ sub ecx, 1
+ jnz .load_textures_loop
+
+ mov ecx, [ebx + akode.LevelHeader.ObjectCount]
+ test ecx, ecx
+ jz @f
+
+ mov [akode_data.CurrentLevelObjectsPtr], esi
+
+.load_object_textures_loop:
+ mov eax, [esi]
+ add esi, sizeof.akode.Object
+
+ test eax, eax
+ jz .skip_object
+
+ stdcall akode.load_texture, eax
+
+ test eax, eax
+ jnz .skip_object
+ mov [load_texture_results], eax
+
+.skip_object:
+ sub ecx, 1
+ jnz .load_object_textures_loop
+
+@@:
+ mov ecx, [ebx + akode.LevelHeader.TextureCount]
+ test ecx, ecx
+ jz @f
+
+ mov [akode_data.CurrentLevelAddTexturesPtr], esi
+
+.load_additional_textures_loop:
+ mov eax, [esi]
+ add esi, 4
+
+ test eax, eax
+ jz .skip_additional
+
+ stdcall akode.load_texture, eax
+
+ test eax, eax
+ jnz .skip_additional
+ mov [load_texture_results], eax
+
+.skip_additional:
+ sub ecx, 1
+ jnz .load_additional_textures_loop
+
+@@:
+ mov edi, [akode_data.BlockSize.Width]
+ shr edi, 1
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+
+ mov eax, [ebx + akode.LevelHeader.StartPosition.X]
+ shl eax, cl
+ add eax, edi
+ mov [akode_data.Camera.Position.X], eax
+
+ mov eax, [ebx + akode.LevelHeader.StartPosition.Y]
+ shl eax, cl
+ add eax, edi
+ mov [akode_data.Camera.Position.Y], eax
+
+ mov eax, [ebx + akode.LevelHeader.StartDirection]
+ imul eax, [akode_data.Angle90]
+ mov [akode_data.Camera.Direction], eax
+
+ xor eax, eax
+ dec eax
+ mov [akode_data.MovementDirection], eax
+ mov [akode_data.TurningDirection], eax
+
+ stdcall akode.set_shading, [ebx + akode.LevelHeader.ShadingColor], [ebx + akode.LevelHeader.ShadingDistance]
+ test eax, eax
+ jz @f
+ mov eax, [load_texture_results]
+@@:
+
+ test edx, edx
+ jz @f
+ stdcall edx, AKODE_LEVEL_LOAD.END, eax
+
+@@:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.unload_current_level uses eax ecx edx esi edi
+ mov esi, [akode_data.CurrentLevelGridPtr]
+ test esi, esi
+ jz .exit
+
+ mov ecx, [akode_data.CurrentLevel.Size.Width]
+ imul ecx, [akode_data.CurrentLevel.Size.Height]
+
+.unload_textures_loop:
+ mov edi, [esi]
+ add esi, 4
+
+ test edi, edi
+ jz .skip
+
+ mov edx, 6 * 2 ; 6 combined textures per cell
+
+.unload_cell_textures_loop:
+ mov eax, [edi]
+ add edi, 4
+
+ test eax, eax
+ jz @f
+
+ stdcall akode.unload_texture, eax
+
+@@:
+ sub edx, 1
+ jnz .unload_cell_textures_loop
+
+.skip:
+ sub ecx, 1
+ jnz .unload_textures_loop
+
+ mov [akode_data.CurrentLevelGridPtr], ecx
+
+ mov ecx, [akode_data.CurrentLevel.ObjectCount]
+ test ecx, ecx
+ jz @f
+
+.unload_object_textures_loop:
+ mov eax, [esi]
+ add esi, sizeof.akode.Object
+
+ test eax, eax
+ jz .skip_object
+
+ stdcall akode.unload_texture, eax
+
+.skip_object:
+ sub ecx, 1
+ jnz .unload_object_textures_loop
+
+@@:
+ mov [akode_data.CurrentLevelObjectsPtr], ecx
+
+ mov ecx, [akode_data.CurrentLevel.TextureCount]
+ test ecx, ecx
+ jz @f
+
+.unload_additional_textures_loop:
+ mov eax, [esi]
+ add esi, 4
+
+ test eax, eax
+ jz .skip_additional
+
+ stdcall akode.unload_texture, eax
+
+.skip_additional:
+ sub ecx, 1
+ jnz .unload_additional_textures_loop
+
+@@:
+ mov [akode_data.CurrentLevelAddTexturesPtr], ecx
+
+ mov eax, [akode_data.CurrentLevel.DestroyCallback]
+ test eax, eax
+ jz @f
+ stdcall eax
+
+@@:
+ mov edx, [akode_data.LevelLoadCallback]
+ test edx, edx
+ jz @f
+ stdcall edx, AKODE_LEVEL_LOAD.UNLOADED, eax
+
+@@:
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 0 - fail ;
+; ============================================================================ ;
+proc akode.load_texture uses ebx ecx edx, texture_desc_ptr
+ mov ebx, [texture_desc_ptr]
+
+ mov eax, [ebx + akode.TextureDesc.Type]
+ or eax, [ebx + akode.TextureDesc.ImageDataPtr]
+ jnz .exit
+
+ mov ecx, [akode_data.BlockSize.Width]
+ mov edx, ecx
+
+ mov al, [ebx + akode.TextureDesc.Usage]
+ test al, al
+ jz @f
+ mov edx, [akode_data.BlockSize.Height]
+
+@@:
+ stdcall akode.load_and_scale_image, [ebx + akode.TextureDesc.ImagePathPtr], ecx, edx, 1
+
+ mov [ebx + akode.TextureDesc.ImageDataPtr], eax
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.unload_texture uses eax ebx ecx, texture_desc_ptr
+ mov ebx, [texture_desc_ptr]
+
+ mov eax, [ebx + akode.TextureDesc.Type]
+ test eax, eax
+ jnz .exit
+
+ mov ecx, [ebx + akode.TextureDesc.ImageDataPtr]
+ test ecx, ecx
+ jz .exit
+
+ push ebx
+ mcall 68, 13
+ pop ebx
+
+ xor eax, eax
+ mov [ebx + akode.TextureDesc.ImageDataPtr], eax
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = pointer to image data / 0 - fail ;
+; ============================================================================ ;
+proc akode.load_and_scale_image uses ebx ecx edx esi edi, image_path_ptr, width, height, internal_format
+ locals
+ decode_options ImageDecodeOptions sizeof.ImageDecodeOptions, 0FF00FFh
+ endl
+
+ stdcall akode.load_file, [image_path_ptr]
+ test eax, eax
+ jz .exit
+
+ mov esi, eax ; esi = file data
+
+ lea eax, [decode_options]
+ push esi
+ invoke img_decode, esi, ebx, eax
+ pop esi
+ test eax, eax
+ jz .exit_and_free_file_fail
+
+ mov edi, eax ; edi = image handle
+
+ xor eax, eax
+ push esi edi
+ invoke img_convert, edi, eax, Image.bpp24, eax, eax
+ pop edi esi
+ push eax
+
+ push esi
+ invoke img_destroy, edi ; destroy handle from img_decode
+ pop ecx
+ mcall 68, 13
+
+ pop edi ; edi = image handle from img_convert
+ test edi, edi
+ jz .exit_fail
+
+ mov ebx, [width]
+ mov ecx, [height]
+
+ cmp [edi + Image.Width], ebx
+ jne .scale
+ cmp [edi + Image.Height], ecx
+ jne .scale
+ jmp @f
+
+.scale:
+ xor eax, eax
+ push edi
+ invoke img_scale, edi, eax, eax, [edi + Image.Width], [edi + Image.Height], eax, LIBIMG_SCALE_STRETCH, LIBIMG_INTER_BILINEAR, ebx, ecx
+ pop edi
+ push eax
+
+ invoke img_destroy, edi ; destroy handle from img_convert
+
+ pop edi ; edi = image handle from img_scale
+ test edi, edi
+ jz .exit_fail
+
+@@:
+ mov eax, [internal_format]
+ test eax, eax
+ jz @f
+
+ invoke img_flip, edi, FLIP_HORIZONTAL
+ invoke img_rotate, edi, ROTATE_90_CCW
+
+@@:
+ stdcall akode._.convert_image_to_32bpp, edi
+ push eax
+
+ invoke img_destroy, edi
+
+ pop eax
+ jmp .exit
+
+.exit_and_free_file_fail:
+ mcall 68, 13, esi
+.exit_fail:
+ xor eax, eax
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = pointer to image data / 0 - fail ;
+; ============================================================================ ;
+proc akode._.convert_image_to_32bpp uses ebx ecx edx esi edi, image
+ mov edi, [image]
+
+ invoke img_to_rgb, edi
+ test eax, eax
+ jz .exit
+
+ mov esi, eax
+
+ mov ecx, [edi + Image.Width]
+ imul ecx, [edi + Image.Height]
+ shl ecx, 2
+
+ mcall 68, 12
+ test eax, eax
+ jz .exit_and_free_fail
+
+ push eax
+ mov edi, eax
+ mov edx, esi
+ add esi, 8
+ shr ecx, 2
+
+@@:
+ mov ax, [esi]
+ movzx bx, byte [esi + 2]
+
+ mov [edi], ax
+ mov [edi + 2], bx
+
+ add esi, 3
+ add edi, 4
+
+ sub ecx, 1
+ jnz @b
+
+ mcall 68, 13, edx
+ pop eax
+ jmp .exit
+
+.exit_and_free_fail:
+ mcall 68, 13, esi
+ xor eax, eax
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = pointer to data / 0 - fail ;
+; < ebx = file size ;
+; ============================================================================ ;
+proc akode.load_file uses ecx edx esi edi, file_path_ptr
+ invoke file_size, [file_path_ptr]
+ test eax, eax
+ jnz .exit_fail
+
+ mov edx, ebx ; edx = file size
+
+ mcall 68, 12, edx
+ test eax, eax
+ jz .exit
+
+ mov edi, eax ; edi = pointer to data
+
+ invoke file_open, [file_path_ptr], O_READ
+ test eax, eax
+ jz .exit_and_free_fail
+
+ mov esi, eax ; esi = file handle
+ mov ebx, edx
+
+ invoke file_read, esi, edi, edx
+ sub edx, eax
+
+ invoke file_close, esi
+
+ test edx, edx
+ jnz .exit_and_free_fail
+
+ mov eax, edi
+ jmp .exit
+
+.exit_and_free_fail:
+ mcall 68, 13, edi
+.exit_fail:
+ xor eax, eax
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 0 - fail ;
+; ============================================================================ ;
+proc akode._.calc_tables uses ebx ecx edx esi edi
+ mov eax, [akode_data.Angle360]
+ mov ecx, eax
+ mov edi, eax
+
+ shr eax, 1
+ mov [akode_data.Angle180], eax
+ mov ebx, eax
+
+ shr eax, 1
+ mov [akode_data.Angle90], eax
+
+ add ebx, eax
+ mov [akode_data.Angle270], ebx
+
+ DEBUGF DEBUG_FINE, 'akode_data.ProjectionPlane.Size.Width: %u\n', [akode_data.ProjectionPlane.Size.Width]
+ DEBUGF DEBUG_FINE, 'akode_data.Camera.FieldOfView: %u\n', [akode_data.Camera.FieldOfView]
+ DEBUGF DEBUG_FINE, 'akode_data.Angle[90, 180, 270, 360]: %u, %u, %u, %u\n', [akode_data.Angle90], [akode_data.Angle180], \
+ [akode_data.Angle270], [akode_data.Angle360]
+
+ shl ecx, 5 ; ecx = Angle360 * (8 bytes * 4 columns)
+ mcall 68, 20, , [akode_data.TrigonometricTablePtr] ; realloc ecx bytes
+
+ test eax, eax
+ jz .exit
+
+ mov [akode_data.TrigonometricTablePtr], eax
+
+ fninit
+ xor ecx, ecx
+ fldpi
+ fldpi
+ faddp
+ fidiv [akode_data.Angle360] ; 2 * pi / Angle360
+@@: ; calculate sin, cos, tan, 1 / cos
+ fld st0
+ mov [eax], ecx
+
+ fimul dword [eax] ; 2 * pi / Angle360 * ecx = ecx in radians
+
+ fsincos
+ fst qword [eax + 8] ; cos
+
+ fld st0
+ fld1
+ fdivrp
+ fstp qword [eax + 24] ; 1 / cos
+
+ fxch
+ fst qword [eax] ; sin
+ fdivrp
+ fstp qword [eax + 16] ; tan
+
+ add ecx, 1
+ add eax, 32
+ cmp ecx, edi
+ jne @b
+
+ ;mov ecx, edi
+ shl ecx, 3 ; ecx = Angle360 * (4 bytes * 2 columns)
+ mcall 68, 20, , [akode_data.BlockWidthTanTablePtr] ; realloc ecx bytes
+
+ test eax, eax
+ jz .pop_fpu_exit
+
+ mov [akode_data.BlockWidthTanTablePtr], eax
+
+ mov edx, [akode_data.Angle180]
+ mov ebx, [akode_data.Angle90]
+ mov esi, [akode_data.Angle270]
+ xor ecx, ecx
+@@: ; calculate BlockSize.Width * tan and BlockSize.Width / tan
+ fld st0
+ mov [eax], ecx
+
+ fimul dword [eax] ; 2 * pi / Angle360 * ecx = ecx in radians
+
+ fptan
+ fld st1
+ fimul [akode_data.BlockSize.Width]
+ fistp dword [eax] ; BlockSize.Width * tan
+
+ cmp ecx, ebx
+ jb .reverse_sign
+ cmp ecx, esi
+ ja .reverse_sign
+ jmp .dont_reverse_sign
+.reverse_sign:
+ neg dword [eax] ; reverse sign for angles < 90 and > 270
+.dont_reverse_sign:
+
+ fdivrp
+ fimul [akode_data.BlockSize.Width]
+ fistp dword [eax + 4] ; BlockSize.Width * 1 / tan
+
+ cmp ecx, edx
+ jna .dont_reverse_sign2
+ neg dword [eax + 4] ; reverse sign for angles > 180
+.dont_reverse_sign2:
+
+ add ecx, 1
+ add eax, 8
+ cmp ecx, edi
+ jne @b
+
+.pop_fpu_exit:
+ fstp st0
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.set_field_of_view fov
+ ; not supported
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 0 - fail ;
+; ============================================================================ ;
+proc akode.set_shading uses ebx ecx edx, color, distance
+ m2m [akode_data.ShadingColor], [color]
+
+ mov ecx, [distance]
+ mov [akode_data.ShadingDistance], ecx
+
+ inc ecx
+ shl ecx, 2
+
+ mcall 68, 20, , [akode_data.ShadingTablePtr] ; realloc ecx bytes
+ test eax, eax
+ jz .exit
+
+ mov [akode_data.ShadingTablePtr], eax
+
+ fninit
+ xor ecx, ecx
+ cmp [distance], ecx
+ jne @f
+ mov dword [eax], 255
+ jmp .exit
+
+@@:
+ imul ebx, ecx, 255
+ mov [eax], ebx
+ fild dword [eax]
+ fidiv [distance]
+ fistp dword [eax]
+
+ mov ebx, 255
+ sub ebx, [eax]
+ mov [eax], ebx
+
+ add ecx, 1
+ add eax, 4
+ cmp [distance], ecx
+ jae @b
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > movement_speed = speed in px per second ;
+; > turning_speed = speed in degrees per second ;
+; ============================================================================ ;
+proc akode.set_movement_speed uses eax ebx edx, movement_speed, turning_speed
+ mov eax, [turning_speed]
+ mul [akode_data.Angle360]
+ mov ebx, 360
+ div ebx
+ mov [akode_data.TurningSpeed], eax
+
+ m2m [akode_data.MovementSpeed], [movement_speed]
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > movement_direction = AKODE_DIRECTION.NORTH - forward, ;
+; AKODE_DIRECTION.SOUTH - backward ;
+; ============================================================================ ;
+proc akode.start_moving uses eax ebx, movement_direction
+ m2m [akode_data.MovementDirection], [movement_direction]
+ mcall 26, 9
+ mov [akode_data.LastMoveTimestamp], eax
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.stop_moving
+ mov [akode_data.MovementDirection], -1
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > turning_direction = AKODE_DIRECTION.WEST - left, ;
+; AKODE_DIRECTION.EAST - right ;
+; ============================================================================ ;
+proc akode.start_turning uses eax ebx, turning_direction
+ m2m [akode_data.TurningDirection], [turning_direction]
+ mcall 26, 9
+ mov [akode_data.LastTurnTimestamp], eax
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.stop_turning
+ mov [akode_data.TurningDirection], -1
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.process uses eax ebx ecx edx esi edi
+ stdcall akode._.process_moving
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode._.process_moving
+ locals
+ timestamp dd ?
+ collision_distance dd ?
+ current_cell_x dd ?
+ current_cell_y dd ?
+ endl
+
+ mov esi, [akode_data.MovementDirection]
+ mov edi, [akode_data.TurningDirection]
+ mov eax, esi
+ and eax, edi
+ js .exit
+
+ mcall 26, 9 ; get timestamp
+ mov [timestamp], eax
+
+ test edi, edi
+ js .not_turning
+
+ mov ebx, eax
+ sub ebx, [akode_data.LastTurnTimestamp]
+ cmp ebx, 4
+ jb .not_turning
+
+ mov [akode_data.LastTurnTimestamp], eax
+
+ mov eax, ebx
+ mul [akode_data.TurningSpeed]
+ mov ecx, 100
+ div ecx ; eax = turn angle
+
+ test eax, eax
+ jnz @f
+ inc eax ; clamp
+@@:
+
+ cmp [akode_data.Angle90], eax
+ jae @f
+ mov eax, [akode_data.Angle90] ; clamp
+@@:
+
+ mov edx, [akode_data.Camera.Direction]
+ mov ecx, [akode_data.Angle360]
+
+ test edi, edi
+ jz .turn_right
+
+ ; turn left
+ add edx, eax
+ cmp ecx, edx
+ ja @f
+ sub edx, ecx
+@@:
+ jmp @f
+
+.turn_right:
+ sub edx, eax
+ jns @f
+ add edx, ecx
+@@:
+ mov [akode_data.Camera.Direction], edx
+
+ mov eax, [timestamp]
+
+.not_turning:
+ test esi, esi
+ js .exit
+
+ mov ebx, eax
+ sub ebx, [akode_data.LastMoveTimestamp]
+ cmp ebx, 4
+ jb .exit
+
+ mov [akode_data.LastMoveTimestamp], eax
+
+ mov eax, ebx
+ mul [akode_data.MovementSpeed]
+ mov ecx, 100
+ div ecx ; eax = move distance
+
+ cmp eax, 3
+ jae @f
+ mov eax, 3 ; clamp
+@@:
+
+ mov edx, [akode_data.BlockSize.Width]
+ shr edx, 2
+ mov ebx, edx
+ sub edx, 2
+ cmp edx, eax
+ jae @f
+ mov eax, edx ; clamp
+@@:
+
+ mov [collision_distance], ebx
+
+ fninit
+
+ mov ebx, [akode_data.Camera.Direction]
+ shl ebx, 5
+ add ebx, [akode_data.TrigonometricTablePtr]
+
+ push eax
+ fild dword [esp]
+ fld st0
+ fmul qword [ebx]
+ fistp dword [esp]
+ mov ecx, [esp] ; ecx = distance * sin (Camera.Direction) = y displacement
+
+ fmul qword [ebx + 8]
+ fistp dword [esp]
+ pop edx ; edx = distance * cos (Camera.Direction) = x displacement
+
+ cmp esi, AKODE_DIRECTION.NORTH
+ mov esi, [akode_data.Camera.Position.X]
+ mov edi, [akode_data.Camera.Position.Y]
+ mov eax, esi
+ mov ebx, edi
+ jne .move_backward
+
+ ; move forward
+ add esi, edx
+ sub edi, ecx
+ jmp @f
+
+.move_backward:
+ sub esi, edx
+ add edi, ecx
+@@:
+
+ ; collision detection
+ locals
+ top_border dd 0
+ left_border dd 0
+ right_border dd 0
+ bottom_border dd 0
+ new_x dd ?
+ new_y dd ?
+ endl
+
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+ mov edx, [akode_data.CurrentLevelGridPtr]
+ shr eax, cl
+ shr ebx, cl
+ mov [current_cell_x], eax
+ mov [current_cell_y], ebx
+
+ ; top border
+ mov eax, esi
+ mov ebx, edi
+ sub ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ shr edi, cl
+ shl edi, cl
+ add edi, [collision_distance]
+ mov [top_border], 1
+@@:
+
+ ; left border
+ mov eax, esi
+ mov ebx, edi
+ sub eax, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ shr esi, cl
+ shl esi, cl
+ add esi, [collision_distance]
+ mov [left_border], 1
+@@:
+
+ ; right border
+ mov eax, esi
+ mov ebx, edi
+ add eax, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ shr esi, cl
+ shl esi, cl
+ add esi, [akode_data.BlockSize.Width]
+ sub esi, [collision_distance]
+ sub esi, 1
+ mov [right_border], 1
+@@:
+
+ ; bottom border
+ mov eax, esi
+ mov ebx, edi
+ add ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ shr edi, cl
+ shl edi, cl
+ add edi, [akode_data.BlockSize.Width]
+ sub edi, [collision_distance]
+ sub edi, 1
+ mov [bottom_border], 1
+@@:
+
+ ; top left border
+ mov eax, [top_border]
+ or eax, [left_border]
+ jnz @f
+
+ mov eax, esi
+ mov ebx, edi
+ sub eax, [collision_distance]
+ sub ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ mov eax, esi
+ mov ebx, edi
+
+ shr eax, cl
+ shl eax, cl
+ add eax, [collision_distance]
+ mov [new_x], eax
+ shr ebx, cl
+ shl ebx, cl
+ add ebx, [collision_distance]
+ mov [new_y], ebx
+
+ sub eax, esi
+ sub ebx, edi
+ cmp eax, ebx
+ jb .l1
+ mov edi, [new_y]
+ jmp @f
+.l1: mov esi, [new_x]
+@@:
+
+ ; top right border
+ mov eax, [top_border]
+ or eax, [right_border]
+ jnz @f
+
+ mov eax, esi
+ mov ebx, edi
+ add eax, [collision_distance]
+ sub ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ mov eax, esi
+ mov ebx, edi
+
+ shr eax, cl
+ shl eax, cl
+ add eax, [akode_data.BlockSize.Width]
+ sub eax, [collision_distance]
+ sub eax, 1
+ mov [new_x], eax
+ shr ebx, cl
+ shl ebx, cl
+ add ebx, [collision_distance]
+ mov [new_y], ebx
+
+ neg eax
+ add eax, esi
+ sub ebx, edi
+ cmp eax, ebx
+ jb .l2
+ mov edi, [new_y]
+ jmp @f
+.l2: mov esi, [new_x]
+@@:
+
+ ; bottom left border
+ mov eax, [bottom_border]
+ or eax, [left_border]
+ jnz @f
+
+ mov eax, esi
+ mov ebx, edi
+ sub eax, [collision_distance]
+ add ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ mov eax, esi
+ mov ebx, edi
+
+ shr eax, cl
+ shl eax, cl
+ add eax, [collision_distance]
+ mov [new_x], eax
+ shr ebx, cl
+ shl ebx, cl
+ add ebx, [akode_data.BlockSize.Width]
+ sub ebx, [collision_distance]
+ sub ebx, 1
+ mov [new_y], ebx
+
+ sub eax, esi
+ neg ebx
+ add ebx, edi
+ cmp eax, ebx
+ jb .l3
+ mov edi, [new_y]
+ jmp @f
+.l3: mov esi, [new_x]
+@@:
+
+ ; bottom right border
+ mov eax, [bottom_border]
+ or eax, [right_border]
+ jnz @f
+
+ mov eax, esi
+ mov ebx, edi
+ add eax, [collision_distance]
+ add ebx, [collision_distance]
+
+ shr eax, cl
+ shr ebx, cl
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [edx + ebx * 4] ; eax = pointer to akode.GridCell
+ test eax, eax
+ jz @f
+ mov ebx, [eax + akode.GridCell.Passable]
+ test ebx, ebx
+ jnz @f
+ mov eax, esi
+ mov ebx, edi
+
+ shr eax, cl
+ shl eax, cl
+ add eax, [akode_data.BlockSize.Width]
+ sub eax, [collision_distance]
+ sub eax, 1
+ mov [new_x], eax
+ shr ebx, cl
+ shl ebx, cl
+ add ebx, [akode_data.BlockSize.Width]
+ sub ebx, [collision_distance]
+ sub ebx, 1
+ mov [new_y], ebx
+
+ neg eax
+ add eax, esi
+ neg ebx
+ add ebx, edi
+ cmp eax, ebx
+ jb .l4
+ mov edi, [new_y]
+ jmp @f
+.l4: mov esi, [new_x]
+@@:
+
+ mov eax, esi
+ mov ebx, edi
+ shr eax, cl
+ shr ebx, cl
+
+ cmp [current_cell_x], eax
+ jne .new_cell
+ cmp [current_cell_y], ebx
+ jne .new_cell
+
+ mov [akode_data.Camera.Position.X], esi
+ mov [akode_data.Camera.Position.Y], edi
+ jmp .exit
+
+.new_cell:
+ stdcall akode.action, AKODE_ACTION.CELL_LEAVE
+
+ mov [akode_data.Camera.Position.X], esi
+ mov [akode_data.Camera.Position.Y], edi
+
+ stdcall akode.action, AKODE_ACTION.CELL_ENTER
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > action = AKODE_ACTION ;
+; ---------------------------------------------------------------------------- ;
+; < eax = action result ;
+; ============================================================================ ;
+proc akode.action uses ebx ecx edx esi edi, action
+ mov edi, [action]
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+ mov ebx, [akode_data.Camera.Position.X]
+ mov edx, [akode_data.Camera.Position.Y]
+ shr ebx, cl
+ shr edx, cl
+
+ mov eax, edx
+ imul eax, [akode_data.CurrentLevel.Size.Width]
+ add eax, ebx
+ mov esi, [akode_data.CurrentLevelGridPtr]
+ mov esi, [esi + eax * 4]
+ test esi, esi
+ jz .default_action
+
+ mov eax, [esi + akode.GridCell.ActionCallback]
+ test eax, eax
+ jz .default_action
+ stdcall eax, edi, ebx, edx
+ cmp eax, -1
+ jne @f
+
+.default_action:
+ mov eax, [akode_data.CurrentLevel.ActionCallback]
+ test eax, eax
+ jz @f
+ stdcall eax, edi, ebx, edx
+
+@@:
+ mov esi, [akode_data.ActionCallback]
+ test esi, esi
+ jz @f
+ stdcall esi, edi, ebx, edx, eax
+
+@@:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode.fill_with_color uses eax ecx edi, color
+ cld
+ mov eax, [color]
+ mov edi, [akode_data.ImageBufferPtr]
+ mov ecx, [akode_data.ImageBufferSize]
+ shr ecx, 2
+ rep stosd
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > eax = color 1 ;
+; > ebx = color 2 ;
+; > edx = alpha, 255 -> color 1, 0 -> color 2 ;
+; ---------------------------------------------------------------------------- ;
+; < eax = result color ;
+; ============================================================================ ;
+macro akode._.blend_colors
+{
+ push ebx ecx edx esi edi
+
+ mov ecx, 256
+ sub ecx, edx
+ add edx, 1
+
+ mov edi, eax
+ and edi, 0FF00FFh
+ imul edi, edx
+
+ mov esi, ebx
+ and esi, 0FF00FFh
+ imul esi, ecx
+
+ add edi, esi
+ and edi, 0FF00FF00h
+
+ and eax, 00FF00h
+ imul eax, edx
+
+ and ebx, 00FF00h
+ imul ebx, ecx
+
+ add eax, ebx
+ and eax, 0FF0000h
+ or eax, edi
+ shr eax, 8
+
+ pop edi esi edx ecx ebx
+}
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > eax = color 1 ;
+; > ebx = color 2 ;
+; > edx = alpha, 255 -> color 1, 0 -> color 2 ;
+; ---------------------------------------------------------------------------- ;
+; < eax = result color ;
+; ============================================================================ ;
+macro akode._.blend_colors_mmx
+{
+ movd mm0, eax
+ movd mm1, ebx
+ pxor mm2, mm2
+ punpcklbw mm0, mm2
+ punpcklbw mm1, mm2
+
+ mov eax, 256
+ sub eax, edx
+ movd mm2, eax
+ pshufw mm3, mm2, 0
+
+ mov eax, edx
+ add eax, 1
+ movd mm4, eax
+ pshufw mm2, mm4, 0
+
+ pmullw mm1, mm3
+ pmullw mm0, mm2
+ paddw mm0, mm1
+ psrlw mm0, 8
+ packuswb mm0, mm0
+ movd eax, mm0
+
+ ;emms
+}
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = pointer to image / 0 - fail ;
+; ============================================================================ ;
+proc akode.render uses ebx ecx edx esi edi
+ mov eax, [akode_data.CurrentLevelGridPtr]
+ test eax, eax
+ jz .exit
+
+ mov eax, [akode_data.CurrentLevel.BackgroundColor]
+ cmp eax, 0FF00FFh
+ je @f
+ stdcall akode.fill_with_color, eax
+
+@@:
+ mov eax, [akode_data.ProjectionPlane.Size.Width]
+ mov ebx, eax
+ sub ebx, 1
+ shr eax, 1
+ add eax, [akode_data.Camera.Direction]
+ mov edx, [akode_data.Angle360]
+ cmp edx, eax
+ ja @f
+ sub eax, edx
+
+@@:
+ push eax ; start_angle for akode._.draw_objects
+ stdcall akode._.render_slices, 0, ebx, eax
+ stdcall akode._.draw_objects
+
+ mov eax, [akode_data.ImageBufferPtr]
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+macro akode._.draw_textured_wall_slice
+{
+ label .int_part dword at x
+ label .fract_part dword at y
+ label .e dword at x_delta
+ label .target_height dword at wall_slice_height
+
+ mov eax, [esi + akode.TextureDesc.ImageDataPtr]
+ mov [texture_desc_ptr1], eax
+
+ mov esi, [texture_desc_ptr2]
+ test esi, esi
+ jz .draw_one_texture
+
+ mov eax, [esi + akode.TextureDesc.Type]
+ test eax, eax
+ jnz .draw_one_texture
+
+ movzx eax, [esi + akode.TextureDesc.HasMagicPink]
+ test eax, eax
+ jnz @f
+ mov eax, [esi + akode.TextureDesc.ImageDataPtr]
+ mov [texture_desc_ptr1], eax
+ jmp .draw_one_texture
+
+@@:
+ ; draw 2 textures
+
+ push edx
+
+ mov esi, [esi + akode.TextureDesc.ImageDataPtr]
+ mov edx, [texture_x_coordinate]
+ mov eax, [akode_data.BlockSize.Width]
+ imul edx, eax
+ shl edx, 2
+ add esi, edx
+ add [texture_desc_ptr1], edx
+
+ xor edx, edx
+ div [.target_height]
+ shl eax, 2
+ mov [.int_part], eax
+ mov [.fract_part], edx
+
+ pop edx
+
+ xor eax, eax
+ mov [.e], eax
+
+ mov eax, [wall_slice_y]
+ test eax, eax
+ js .clipping2
+
+.draw_double_textured_wall_slice_loop:
+ mov eax, [esi]
+ cmp eax, 0FF00FFh
+ jne @f
+ mov eax, [texture_desc_ptr1]
+ mov eax, [eax]
+
+@@:
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors_mmx
+end if
+
+ mov [edi], eax
+
+ add edi, 4
+ mov eax, [.int_part]
+ add esi, eax
+ add [texture_desc_ptr1], eax
+ mov eax, [.e]
+ add eax, [.fract_part]
+ mov [.e], eax
+
+ sub eax, [.target_height]
+ jl @f
+ mov [.e], eax
+ add esi, 4
+ add [texture_desc_ptr1], 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_double_textured_wall_slice_loop
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ emms
+end if
+
+ jmp .draw_floor_slice
+
+.clipping2:
+ sub ecx, [wall_slice_y]
+
+.draw_double_textured_wall_slice_loop2:
+ mov eax, [wall_slice_y]
+ test eax, eax
+ js .skip_pixel
+
+ mov eax, [esi]
+ cmp eax, 0FF00FFh
+ jne @f
+ mov eax, [texture_desc_ptr1]
+ mov eax, [eax]
+
+@@:
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors_mmx
+end if
+
+ mov [edi], eax
+ add edi, 4
+.skip_pixel:
+ add [wall_slice_y], 1
+ mov eax, [.int_part]
+ add esi, eax
+ add [texture_desc_ptr1], eax
+ mov eax, [.e]
+ add eax, [.fract_part]
+ mov [.e], eax
+
+ sub eax, [.target_height]
+ jl @f
+ mov [.e], eax
+ add esi, 4
+ add [texture_desc_ptr1], 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_double_textured_wall_slice_loop2
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ emms
+end if
+
+ jmp .draw_floor_slice
+
+.draw_one_texture:
+ push edx
+
+ mov esi, [texture_desc_ptr1]
+ mov edx, [texture_x_coordinate]
+ mov eax, [akode_data.BlockSize.Width]
+ imul edx, eax
+ lea esi, [esi + edx * 4]
+
+ xor edx, edx
+ div [.target_height]
+ shl eax, 2
+ mov [.int_part], eax
+ mov [.fract_part], edx
+
+ pop edx
+
+ xor eax, eax
+ mov [.e], eax
+
+ mov eax, [wall_slice_y]
+ test eax, eax
+ js .clipping
+
+.draw_textured_wall_slice_loop:
+ mov eax, [esi]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors_mmx
+end if
+
+ mov [edi], eax
+
+ add edi, 4
+ add esi, [.int_part]
+ mov eax, [.e]
+ add eax, [.fract_part]
+ mov [.e], eax
+
+ sub eax, [.target_height]
+ jl @f
+ mov [.e], eax
+ add esi, 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_textured_wall_slice_loop
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ emms
+end if
+
+ jmp .draw_floor_slice
+
+.clipping:
+ sub ecx, [wall_slice_y]
+
+.draw_textured_wall_slice_loop2:
+ mov eax, [wall_slice_y]
+ test eax, eax
+ js @f
+ mov eax, [esi]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors_mmx
+end if
+
+ mov [edi], eax
+ add edi, 4
+@@:
+ add [wall_slice_y], 1
+ add esi, [.int_part]
+ mov eax, [.e]
+ add eax, [.fract_part]
+ mov [.e], eax
+
+ sub eax, [.target_height]
+ jl @f
+ mov [.e], eax
+ add esi, 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_textured_wall_slice_loop2
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ emms
+end if
+}
+
+;void ScaleLine(PIXEL *Target, PIXEL *Source, int SrcWidth, int TgtWidth)
+;{
+; int NumPixels = TgtWidth;
+; int IntPart = SrcWidth / TgtWidth;
+; int FractPart = SrcWidth % TgtWidth;
+; int E = 0;
+;
+; while (NumPixels-- > 0) {
+; *Target++ = *Source;
+; Source += IntPart;
+; E += FractPart;
+; if (E >= TgtWidth) {
+; E -= TgtWidth;
+; Source++;
+; } /* if */
+; } /* while */
+;}
+
+proc akode._.render_slices first_slice, last_slice, start_angle
+ locals
+ distance_to_floor dd ?
+ distance_to_wall dd ?
+ horizontal_distance_to_wall dq ?
+ vertical_distance_to_wall dq ?
+
+ texture_x_coordinate dd ?
+ texture_y_coordinate dd ?
+
+ wall_slice_height dd ?
+ wall_slice_y dd ?
+ wall_slice_top dd ?
+ wall_slice_bottom dd ?
+ wall_slice_top_ptr dd ?
+
+ horizontal_texture_desc_ptr1 dd ?
+ horizontal_texture_desc_ptr2 dd ?
+
+ vertical_texture_desc_ptr1 dd ?
+ vertical_texture_desc_ptr2 dd ?
+
+ texture_desc_ptr1 dd ?
+ texture_desc_ptr2 dd ?
+
+ x dd ?
+ y dd ?
+ x_delta dd ?
+ y_delta dd ?
+
+ wall_side dd ?
+ endl
+
+ fninit
+
+.slice_loop:
+ ;DEBUGF DEBUG_FINE, 'first_slice: %u, last_slice: %u, start_angle: %u\n', [first_slice], [last_slice], [start_angle]
+
+; ------------------------------- render walls ------------------------------- ;
+
+ ; horizontal intersections
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+ mov eax, [akode_data.Camera.Position.Y]
+ mov edi, eax
+ shr eax, cl
+ shl eax, cl
+ mov edx, [akode_data.BlockSize.Width]
+
+ mov ebx, [start_angle]
+ test ebx, ebx
+ jz .ray_is_horizontal
+ cmp [akode_data.Angle180], ebx
+ je .ray_is_horizontal
+ jb .ray_is_facing_down
+
+ ; ray is facing up
+
+ sub eax, 1
+ neg edx
+ mov [wall_side], AKODE_DIRECTION.SOUTH
+ jmp @f
+
+ ; ray is facing down
+
+.ray_is_facing_down:
+ add eax, edx ; edx = BlockSize.Width
+ mov [wall_side], AKODE_DIRECTION.NORTH
+
+@@:
+ mov [y], eax
+ mov [y_delta], edx
+
+ sub edi, eax ; edi = Camera.Position.Y - y
+ push edi
+ mov esi, ebx
+ shl esi, 5 ; esi = angle * 32
+ add esi, [akode_data.TrigonometricTablePtr]
+
+ fild dword [esp]
+ fdiv qword [esi + 16]
+ fistp dword [esp]
+ pop edi ; edi = (Camera.Position.Y - y) / tan (angle)
+
+ add edi, [akode_data.Camera.Position.X]
+ mov [x], edi
+
+ mov esi, [akode_data.BlockWidthTanTablePtr]
+ mov eax, [esi + ebx * 8 + 4] ; eax = BlockSize.Width / tan (angle)
+ mov [x_delta], eax
+ ;DEBUGF DEBUG_FINE, 'x_delta: %d\n', eax
+
+.horizontal_intersections_loop:
+ mov eax, [x]
+ mov ebx, [y]
+
+ sar eax, cl
+ js .ray_is_horizontal
+ cmp [akode_data.CurrentLevel.Size.Width], eax
+ jna .ray_is_horizontal
+
+ sar ebx, cl
+ js .ray_is_horizontal
+ cmp [akode_data.CurrentLevel.Size.Height], ebx
+ jna .ray_is_horizontal
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov esi, [akode_data.CurrentLevelGridPtr]
+ mov esi, [esi + ebx * 4] ; esi = pointer to akode.GridCell
+
+ test esi, esi
+ jz .next_horizontal_intersection
+
+ mov eax, [wall_side]
+ lea edi, [esi + eax * 8]
+
+ mov ebx, [edi] ; first texture desc ptr
+ test ebx, ebx
+ jz .next_horizontal_intersection
+
+ mov edx, [edi + 4] ; second texture desc ptr
+
+ mov [horizontal_texture_desc_ptr1], ebx
+ mov [horizontal_texture_desc_ptr2], edx
+
+ mov edx, -1
+ shl edx, cl
+ not edx
+
+ mov esi, [x]
+ and esi, edx
+ cmp eax, AKODE_DIRECTION.NORTH
+ jne @f
+ mov eax, [akode_data.BlockSize.Width]
+ add esi, 1
+ xchg eax, esi
+ sub esi, eax
+@@:
+ mov [texture_x_coordinate], esi
+
+ mov ebx, [start_angle]
+ mov esi, [akode_data.TrigonometricTablePtr]
+
+ mov eax, [akode_data.Camera.Position.Y]
+ sub eax, [y]
+
+ mov edx, ebx
+ shl edx, 5
+
+ push eax
+ fild dword [esp]
+ add esp, 4
+ fdiv qword [esi + edx] ; st0 = (Camera.Position.Y - y) / sin (angle)
+
+ ; correct fisheye
+ mov eax, [akode_data.Camera.Direction]
+ sub eax, ebx
+ jns @f
+ neg eax
+
+@@:
+ shl eax, 5
+ fmul qword [esi + eax + 8] ; st0 * cos (Camera.Direction - angle)
+ fstp [horizontal_distance_to_wall]
+
+ jmp .vertical_intersections
+
+.next_horizontal_intersection:
+ mov eax, [x_delta]
+ mov ebx, [y_delta]
+ add [x], eax
+ add [y], ebx
+ jmp .horizontal_intersections_loop
+
+.ray_is_horizontal:
+ ; horizontal_distance_to_wall = max double
+ mov dword [horizontal_distance_to_wall], 0FFFFFFFFh
+ mov dword [horizontal_distance_to_wall + 4], 7FEFFFFFh
+
+ ; vertical intersections
+.vertical_intersections:
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+ mov eax, [akode_data.Camera.Position.X]
+ mov edi, eax
+ shr eax, cl
+ shl eax, cl
+ mov edx, [akode_data.BlockSize.Width]
+
+ mov ebx, [start_angle]
+ cmp [akode_data.Angle90], ebx
+ je .ray_is_vertical
+ ja .ray_is_facing_right
+ cmp [akode_data.Angle270], ebx
+ je .ray_is_vertical
+ jb .ray_is_facing_right
+
+ ; ray is facing left
+
+ sub eax, 1
+ neg edx
+ mov [wall_side], AKODE_DIRECTION.EAST
+ jmp @f
+
+ ; ray is facing right
+
+.ray_is_facing_right:
+ add eax, edx ; edx = BlockSize.Width
+ mov [wall_side], AKODE_DIRECTION.WEST
+
+@@:
+ mov [x], eax
+ mov [x_delta], edx
+
+ sub edi, eax ; edi = Camera.Position.X - x
+ push edi
+ mov esi, ebx
+ shl esi, 5 ; esi = angle * 32
+ add esi, [akode_data.TrigonometricTablePtr]
+
+ fild dword [esp]
+ fmul qword [esi + 16]
+ fistp dword [esp]
+ pop edi ; edi = (Camera.Position.X - x) * tan (angle)
+
+ add edi, [akode_data.Camera.Position.Y]
+ mov [y], edi
+
+ mov esi, [akode_data.BlockWidthTanTablePtr]
+ mov eax, [esi + ebx * 8] ; eax = BlockSize.Width * tan (angle)
+ mov [y_delta], eax
+ ;DEBUGF DEBUG_FINE, 'y_delta: %d\n', eax
+
+.vertical_intersections_loop:
+ mov eax, [x]
+ mov ebx, [y]
+
+ sar eax, cl
+ js .ray_is_vertical
+ cmp [akode_data.CurrentLevel.Size.Width], eax
+ jna .ray_is_vertical
+
+ sar ebx, cl
+ js .ray_is_vertical
+ cmp [akode_data.CurrentLevel.Size.Height], ebx
+ jna .ray_is_vertical
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov esi, [akode_data.CurrentLevelGridPtr]
+ mov esi, [esi + ebx * 4] ; esi = pointer to akode.GridCell
+
+ test esi, esi
+ jz .next_vertical_intersection
+
+ mov eax, [wall_side]
+ lea edi, [esi + eax * 8]
+
+ mov ebx, [edi] ; first texture desc ptr
+ test ebx, ebx
+ jz .next_vertical_intersection
+
+ mov edx, [edi + 4] ; second texture desc ptr
+
+ mov [vertical_texture_desc_ptr1], ebx
+ mov [vertical_texture_desc_ptr2], edx
+
+ mov edx, -1
+ shl edx, cl
+ not edx
+
+ mov esi, [y]
+ and esi, edx
+ test eax, eax ; cmp eax, AKODE_DIRECTION.EAST
+ jnz @f
+ mov eax, [akode_data.BlockSize.Width]
+ add esi, 1
+ xchg eax, esi
+ sub esi, eax
+@@:
+ mov [texture_y_coordinate], esi
+
+ mov ebx, [start_angle]
+ mov esi, [akode_data.TrigonometricTablePtr]
+
+ mov eax, [x]
+ sub eax, [akode_data.Camera.Position.X]
+
+ mov edx, ebx
+ shl edx, 5
+
+ push eax
+ fild dword [esp]
+ add esp, 4
+ fmul qword [esi + edx + 24] ; st0 = (x - Camera.Position.X) / cos (angle)
+
+ ; correct fisheye
+ mov eax, [akode_data.Camera.Direction]
+ sub eax, ebx
+ jns @f
+ neg eax
+
+@@:
+ shl eax, 5
+ fmul qword [esi + eax + 8] ; st0 * cos (Camera.Direction - angle)
+ fstp [vertical_distance_to_wall]
+
+ jmp .draw_wall_slice
+
+.next_vertical_intersection:
+ mov eax, [x_delta]
+ mov ebx, [y_delta]
+ add [x], eax
+ add [y], ebx
+ jmp .vertical_intersections_loop
+
+.ray_is_vertical:
+ ; vertical_distance_to_wall = max double
+ mov dword [vertical_distance_to_wall], 0FFFFFFFFh
+ mov dword [vertical_distance_to_wall + 4], 7FEFFFFFh
+
+
+; ----------------------------- draw wall slice ------------------------------ ;
+
+.draw_wall_slice:
+ fld [horizontal_distance_to_wall]
+ fld [vertical_distance_to_wall]
+ fcomi st1
+ fcmovnb st1
+ fist [distance_to_wall]
+ fidivr [akode_data.WallHeightDividend]
+ fistp [wall_slice_height]
+ fstp st0
+
+ mov ebx, [akode_data.DepthBufferPtr]
+ mov ecx, [first_slice]
+ lea ebx, [ebx + ecx * 4]
+ mov eax, [distance_to_wall]
+ mov [ebx], eax
+
+ jnb .horizontal_textures
+ mov eax, [vertical_texture_desc_ptr1]
+ mov ebx, [vertical_texture_desc_ptr2]
+
+ mov ecx, [texture_y_coordinate]
+ mov [texture_x_coordinate], ecx
+ jmp @f
+
+.horizontal_textures:
+ mov eax, [horizontal_texture_desc_ptr1]
+ mov ebx, [horizontal_texture_desc_ptr2]
+
+@@:
+ mov ecx, [wall_slice_height]
+ test ecx, ecx
+ jz .skip_draw_wall_slice
+
+ mov [texture_desc_ptr1], eax
+ mov [texture_desc_ptr2], ebx
+
+ mov edx, [akode_data.ProjectionPlane.MidY]
+ mov eax, ecx
+ shr eax, 1
+ sub edx, eax
+ mov [wall_slice_y], edx
+ jns @f
+ xor edx, edx
+
+@@:
+ mov ebx, [akode_data.ProjectionPlane.Size.Height]
+ mov eax, ecx
+ add eax, edx
+ cmp ebx, eax
+ jae @f
+ mov ecx, ebx
+ sub ecx, edx
+
+@@:
+ mov eax, edx
+ sub eax, 1
+ mov [wall_slice_top], eax
+ mov eax, edx
+ add eax, ecx
+ mov [wall_slice_bottom], eax
+
+ mov eax, [akode_data.ProjectionPlane.Size.Height]
+ imul eax, [first_slice]
+ add eax, edx
+ mov edi, [akode_data.ImageBufferPtr]
+ lea edi, [edi + eax * 4]
+ mov eax, edi
+ sub eax, 4
+ mov [wall_slice_top_ptr], eax
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ mov esi, [akode_data.ShadingTablePtr]
+ mov ebx, [akode_data.ShadingDistance]
+ mov eax, [distance_to_wall]
+ cmp ebx, eax
+ jae @f
+ mov eax, ebx
+@@:
+ mov edx, [esi + eax * 4]
+ mov ebx, [akode_data.ShadingColor]
+end if
+
+ mov esi, [texture_desc_ptr1]
+ mov eax, [esi + akode.TextureDesc.Type]
+ test eax, eax
+ jz .texture_type_image
+
+ ; texture type is color
+
+ add esi, akode.TextureDesc.Color
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+.draw_color_wall_slice_loop:
+ mov eax, [esi]
+
+ akode._.blend_colors_mmx
+
+ mov [edi], eax
+ add edi, 4
+
+ sub ecx, 1
+ jnz .draw_color_wall_slice_loop
+
+ emms
+else
+ mov eax, [esi]
+
+.draw_color_wall_slice_loop:
+ mov [edi], eax
+ add edi, 4
+
+ sub ecx, 1
+ jnz .draw_color_wall_slice_loop
+end if
+
+ jmp .draw_floor_slice
+
+.texture_type_image:
+ akode._.draw_textured_wall_slice
+ jmp .draw_floor_slice
+
+.skip_draw_wall_slice:
+ mov eax, [akode_data.ProjectionPlane.MidY]
+ mov [wall_slice_top], eax
+ add eax, 1
+ mov [wall_slice_bottom], eax
+
+ mov eax, [akode_data.ProjectionPlane.Size.Height]
+ imul eax, [first_slice]
+ add eax, [wall_slice_bottom]
+ mov edi, [akode_data.ImageBufferPtr]
+ lea edi, [edi + eax * 4]
+ mov eax, edi
+ sub eax, 4
+ mov [wall_slice_top_ptr], eax
+
+; ----------------------------- draw floor slice ----------------------------- ;
+
+.draw_floor_slice:
+ mov esi, [akode_data.TrigonometricTablePtr]
+ mov ebx, [start_angle]
+ mov eax, ebx
+ shl eax, 5
+ fld qword [esi + eax] ; sin (angle)
+ fld qword [esi + eax + 8] ; cos (angle)
+
+ mov eax, [akode_data.Camera.Direction]
+ sub eax, ebx
+ jns @f
+ neg eax
+
+@@:
+ shl eax, 5
+ fld qword [esi + eax + 24] ; 1 / cos (Camera.Direction - angle)
+
+ mov ecx, [wall_slice_bottom]
+ cmp [akode_data.ProjectionPlane.Size.Height], ecx
+ jbe .skip_draw_floor_slice
+
+ add ecx, 1
+ fild [akode_data.FloorDistanceDividend]
+ fmul st0, st1 ; st0 = FloorDistanceDividend / cos (Camera.Direction - angle)
+
+.draw_floor_slice_loop:
+ mov eax, ecx
+ sub eax, [akode_data.ProjectionPlane.MidY]
+ push eax
+
+ fld st0
+ fidiv dword [esp]
+ fist [distance_to_floor] ; FloorDistanceDividend / cos (Camera.Direction - angle) / (ecx - ProjectionPlane.MidY)
+
+ fld st0
+ fmul st0, st4 ; distance * cos (angle)
+ fistp dword [esp]
+ mov ebx, [esp] ; ebx = x delta
+
+ fmul st0, st4 ; distance * sin (angle)
+ fistp dword [esp]
+ pop edx ; edx = y delta
+
+ mov eax, [akode_data.Camera.Position.X]
+ add eax, ebx
+ mov ebx, [akode_data.Camera.Position.Y]
+ sub ebx, edx
+
+ push ecx
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+
+ mov edx, -1
+ shl edx, cl
+ not edx
+
+ mov esi, eax
+ and esi, edx
+ mov [texture_x_coordinate], esi
+
+ mov esi, ebx
+ and esi, edx
+ mov [texture_y_coordinate], esi
+
+ sar eax, cl
+ js .next_floor_intersection
+ cmp [akode_data.CurrentLevel.Size.Width], eax
+ jna .next_floor_intersection
+
+ sar ebx, cl
+ js .next_floor_intersection
+ cmp [akode_data.CurrentLevel.Size.Height], ebx
+ jna .next_floor_intersection
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [akode_data.CurrentLevelGridPtr]
+ mov eax, [eax + ebx * 4] ; eax = pointer to akode.GridCell
+
+ test eax, eax
+ jz .next_floor_intersection
+
+ mov esi, [eax + akode.GridCell.FloorTexture]
+ test esi, esi
+ jz .next_floor_intersection
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ mov edx, [akode_data.ShadingTablePtr]
+ mov ebx, [akode_data.ShadingDistance]
+ mov ecx, [distance_to_floor]
+ cmp ebx, ecx
+ jae @f
+ mov ecx, ebx
+@@:
+ mov edx, [edx + ecx * 4]
+ mov ebx, [akode_data.ShadingColor]
+end if
+
+ mov eax, [esi + akode.TextureDesc.Type]
+ test eax, eax
+ jz .texture_type_image2
+
+ ; texture type is color
+ mov eax, [esi + akode.TextureDesc.Color]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors
+end if
+
+ mov [edi], eax
+
+ jmp .next_floor_intersection
+
+.texture_type_image2:
+ mov eax, [texture_x_coordinate]
+ imul eax, [akode_data.BlockSize.Width]
+ add eax, [texture_y_coordinate]
+ mov esi, [esi + akode.TextureDesc.ImageDataPtr]
+ mov eax, [esi + eax * 4]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors
+end if
+
+ mov [edi], eax
+
+.next_floor_intersection:
+ pop ecx
+
+ add edi, 4
+ add ecx, 1
+ cmp [akode_data.ProjectionPlane.Size.Height], ecx
+ jae .draw_floor_slice_loop
+
+ fstp st0
+
+.skip_draw_floor_slice:
+
+; ---------------------------- draw ceiling slice ---------------------------- ;
+
+ mov ecx, [wall_slice_top]
+ test ecx, ecx
+ js .skip_draw_ceiling_slice
+
+ sub ecx, 1
+ fild [akode_data.CeilingDistanceDividend]
+ fmul st0, st1 ; st0 = CeilingDistanceDividend / cos (Camera.Direction - angle)
+ mov edi, [wall_slice_top_ptr]
+
+.draw_ceiling_slice_loop:
+ mov eax, [akode_data.ProjectionPlane.MidY]
+ sub eax, ecx
+ push eax
+
+ fld st0
+ fidiv dword [esp]
+ fist [distance_to_floor] ; CeilingDistanceDividend / cos (Camera.Direction - angle) / (ProjectionPlane.MidY - ecx)
+
+ fld st0
+ fmul st0, st4 ; distance * cos (angle)
+ fistp dword [esp]
+ mov ebx, [esp] ; ebx = x delta
+
+ fmul st0, st4 ; distance * sin (angle)
+ fistp dword [esp]
+ pop edx ; edx = y delta
+
+ mov eax, [akode_data.Camera.Position.X]
+ add eax, ebx
+ mov ebx, [akode_data.Camera.Position.Y]
+ sub ebx, edx
+
+ push ecx
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+
+ mov edx, -1
+ shl edx, cl
+ not edx
+
+ mov esi, eax
+ and esi, edx
+ mov [texture_x_coordinate], esi
+
+ mov esi, ebx
+ and esi, edx
+ mov [texture_y_coordinate], esi
+
+ sar eax, cl
+ js .next_ceiling_intersection
+ cmp [akode_data.CurrentLevel.Size.Width], eax
+ jna .next_ceiling_intersection
+
+ sar ebx, cl
+ js .next_ceiling_intersection
+ cmp [akode_data.CurrentLevel.Size.Height], ebx
+ jna .next_ceiling_intersection
+
+ imul ebx, [akode_data.CurrentLevel.Size.Width]
+ add ebx, eax
+ mov eax, [akode_data.CurrentLevelGridPtr]
+ mov eax, [eax + ebx * 4] ; eax = pointer to akode.GridCell
+
+ test eax, eax
+ jz .next_ceiling_intersection
+
+ mov esi, [eax + akode.GridCell.CeilingTexture]
+ test esi, esi
+ jz .next_ceiling_intersection
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ mov edx, [akode_data.ShadingTablePtr]
+ mov ebx, [akode_data.ShadingDistance]
+ mov ecx, [distance_to_floor]
+ cmp ebx, ecx
+ jae @f
+ mov ecx, ebx
+@@:
+ mov edx, [edx + ecx * 4]
+ mov ebx, [akode_data.ShadingColor]
+end if
+
+ mov eax, [esi + akode.TextureDesc.Type]
+ test eax, eax
+ jz .texture_type_image3
+
+ ; texture type is color
+ mov eax, [esi + akode.TextureDesc.Color]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors
+end if
+
+ mov [edi], eax
+
+ jmp .next_ceiling_intersection
+
+.texture_type_image3:
+ mov eax, [texture_x_coordinate]
+ imul eax, [akode_data.BlockSize.Width]
+ add eax, [texture_y_coordinate]
+ mov esi, [esi + akode.TextureDesc.ImageDataPtr]
+ mov eax, [esi + eax * 4]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ akode._.blend_colors
+end if
+
+ mov [edi], eax
+
+.next_ceiling_intersection:
+ pop ecx
+
+ sub edi, 4
+ sub ecx, 1
+ cmp ecx, -1
+ jge .draw_ceiling_slice_loop
+
+ fstp st0
+
+.skip_draw_ceiling_slice:
+
+ fstp st0
+ fstp st0
+ fstp st0
+
+ sub [start_angle], 1
+ jns @f
+ mov edx, [akode_data.Angle360]
+ sub edx, 1
+ mov [start_angle], edx
+
+@@:
+ mov eax, [first_slice]
+ add eax, 1
+ mov [first_slice], eax
+ cmp [last_slice], eax
+ jae .slice_loop
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+macro akode._.draw_object_slice
+{
+; mov eax, [akode_data.BlockSize.Height]
+; xor edx, edx
+; div [object_screen_height]
+; shl eax, 2
+; mov [slice.int_part], eax
+; mov [slice.fract_part], edx
+
+ xor eax, eax
+ mov [slice.e], eax
+ mov ecx, [slice.pixel_count]
+
+.draw_object_slice:
+ mov eax, [slice.y]
+ test eax, eax
+ js .skip_pixel
+
+ mov eax, [esi]
+ cmp eax, 0FF00FFh
+ je @f
+ mov [edi], eax
+@@: add edi, 4
+
+.skip_pixel:
+ add [slice.y], 1
+ add esi, [slice.int_part]
+ mov eax, [slice.e]
+ add eax, [slice.fract_part]
+ mov [slice.e], eax
+
+ sub eax, [object_screen_height]
+ jl @f
+ mov [slice.e], eax
+ add esi, 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_object_slice
+}
+
+macro akode._.draw_object_slice_with_shading
+{
+ xor eax, eax
+ mov [slice.e], eax
+ mov ecx, [slice.pixel_count]
+
+.draw_object_slice2:
+ mov eax, [slice.y]
+ test eax, eax
+ js .skip_pixel2
+
+ mov eax, [esi]
+ cmp eax, 0FF00FFh
+ je @f
+ akode._.blend_colors_mmx
+ mov [edi], eax
+@@: add edi, 4
+
+.skip_pixel2:
+ add [slice.y], 1
+ add esi, [slice.int_part]
+ mov eax, [slice.e]
+ add eax, [slice.fract_part]
+ mov [slice.e], eax
+
+ sub eax, [object_screen_height]
+ jl @f
+ mov [slice.e], eax
+ add esi, 4
+
+@@:
+ sub ecx, 1
+ jnz .draw_object_slice2
+}
+
+macro akode._.draw_object
+{
+ locals
+ slice.int_part dd ?
+ slice.fract_part dd ?
+ slice.e dd ?
+ slice.y dd ?
+ slice.pixel_count dd ?
+
+ int_part dd ?
+ fract_part dd ?
+ e dd ?
+ slice_count dd ?
+ object_distance dd ?
+ endl
+
+ mov eax, [object_screen_height]
+ mov ebx, [object_screen_width]
+
+ cmp eax, 10
+ jb .draw_object_exit
+ cmp ebx, 10
+ jb .draw_object_exit
+
+ mov ecx, [object_screen_x]
+ mov edx, [object_screen_y]
+
+ cmp ecx, [akode_data.ProjectionPlane.Size.Width]
+ jge .draw_object_exit
+ cmp edx, [akode_data.ProjectionPlane.Size.Height]
+ jge .draw_object_exit
+
+ add ecx, ebx
+ add edx, eax
+
+ xor edi, edi
+
+ cmp ecx, edi
+ jle .draw_object_exit
+ cmp edx, edi
+ jle .draw_object_exit
+
+ sub ecx, [akode_data.ProjectionPlane.Size.Width]
+ jbe @f
+ sub ebx, ecx
+@@:
+ mov [slice_count], ebx
+
+ sub edx, [akode_data.ProjectionPlane.Size.Height]
+ jbe @f
+ sub eax, edx
+@@:
+ mov [slice.pixel_count], eax
+
+ mov eax, [akode_data.BlockSize.Height]
+ xor edx, edx
+ div [object_screen_height]
+ shl eax, 2
+ mov [slice.int_part], eax
+ mov [slice.fract_part], edx
+
+ mov eax, [akode_data.BlockSize.Width]
+ xor edx, edx
+ div [object_screen_width]
+ mov [fract_part], edx
+ mul [akode_data.BlockSize.Height]
+ shl eax, 2
+ mov [int_part], eax
+
+ xor eax, eax
+ mov [e], eax
+
+ mov eax, [object_screen_x]
+ cmp eax, edi
+ jge @f
+ xor eax, eax
+
+@@:
+ mov edi, [akode_data.ImageBufferPtr]
+ mul [akode_data.ProjectionPlane.Size.Height]
+ lea edi, [edi + eax * 4]
+
+ mov eax, [esi + akode.Object.Distance]
+ mov [object_distance], eax
+ mov ecx, [esi + akode.Object.ShadingDistance]
+
+ mov eax, [esi + akode.Object.DisableShading]
+ mov esi, [esi + akode.Object.TextureDescPtr]
+ mov esi, [esi + akode.TextureDesc.ImageDataPtr]
+
+if ~(defined DISABLE_SHADING & DISABLE_SHADING)
+ test eax, eax
+ jnz .draw_object_without_shading
+
+ ; draw object with shading
+ mov edx, [akode_data.ShadingTablePtr]
+ mov ebx, [akode_data.ShadingDistance]
+ cmp ebx, ecx
+ jae @f
+ mov ecx, ebx
+@@:
+ mov edx, [edx + ecx * 4]
+ mov ebx, [akode_data.ShadingColor]
+
+.draw_object_slices_loop2:
+ mov eax, [object_screen_x]
+ test eax, eax
+ js .skip_slice2
+
+ mov ecx, [akode_data.DepthBufferPtr]
+ mov eax, [ecx + eax * 4]
+ cmp [object_distance], eax
+ jae .slice_hidden_by_wall2
+
+ push esi
+ push edi
+
+ mov eax, [object_screen_y]
+ mov [slice.y], eax
+
+ test eax, eax
+ jns @f
+ xor eax, eax
+@@: lea edi, [edi + eax * 4]
+
+ akode._.draw_object_slice_with_shading
+
+ pop edi
+ pop esi
+
+.slice_hidden_by_wall2:
+ mov eax, [akode_data.ProjectionPlane.Size.Height]
+ lea edi, [edi + eax * 4]
+
+.skip_slice2:
+ add [object_screen_x], 1
+ add esi, [int_part]
+ mov eax, [e]
+ add eax, [fract_part]
+ mov [e], eax
+
+ sub eax, [object_screen_width]
+ jl @f
+ mov [e], eax
+ mov eax, [akode_data.BlockSize.Height]
+ lea esi, [esi + eax * 4]
+
+@@:
+ sub [slice_count], 1
+ jnz .draw_object_slices_loop2
+
+ emms
+
+ jmp .draw_object_exit
+end if
+
+.draw_object_without_shading:
+
+.draw_object_slices_loop:
+ mov eax, [object_screen_x]
+ test eax, eax
+ js .skip_slice
+
+ mov ecx, [akode_data.DepthBufferPtr]
+ mov eax, [ecx + eax * 4]
+ cmp [object_distance], eax
+ jae .slice_hidden_by_wall
+
+ push esi
+ push edi
+
+ mov eax, [object_screen_y]
+ mov [slice.y], eax
+
+ test eax, eax
+ jns @f
+ xor eax, eax
+@@: lea edi, [edi + eax * 4]
+
+ akode._.draw_object_slice
+
+ pop edi
+ pop esi
+
+.slice_hidden_by_wall:
+ mov eax, [akode_data.ProjectionPlane.Size.Height]
+ lea edi, [edi + eax * 4]
+
+.skip_slice:
+ add [object_screen_x], 1
+ add esi, [int_part]
+ mov eax, [e]
+ add eax, [fract_part]
+ mov [e], eax
+
+ sub eax, [object_screen_width]
+ jl @f
+ mov [e], eax
+ mov eax, [akode_data.BlockSize.Height]
+ lea esi, [esi + eax * 4]
+
+@@:
+ sub [slice_count], 1
+ jnz .draw_object_slices_loop
+
+ .draw_object_exit:
+}
+
+proc akode._.draw_objects start_angle
+ locals
+ camera_cell_x dd ?
+ camera_cell_y dd ?
+
+ quadrants dd ?
+ x_quadrants dd ?
+ y_quadrants dd ?
+
+ object_count dd ?
+
+ object_screen_x_delta dd ?
+ object_screen_x dd ?
+ object_screen_y dd ?
+ object_screen_width dd ?
+ object_screen_height dd ?
+ endl
+
+ mov eax, [akode_data.CurrentLevel.ObjectCount]
+ test eax, eax
+ jz .exit
+
+ fninit
+
+ mov eax, [start_angle]
+
+ cmp [akode_data.Angle0], eax
+ jne @f
+ mov ebx, 1001b
+ jmp .first_quadrant_found
+
+@@: cmp [akode_data.Angle90], eax
+ jne @f
+ mov ebx, 0011b
+ jmp .first_quadrant_found
+
+@@: jb @f
+ mov ebx, 0001b
+ jmp .first_quadrant_found
+
+@@: cmp [akode_data.Angle180], eax
+ jne @f
+ mov ebx, 0110b
+ jmp .first_quadrant_found
+
+@@: jb @f
+ mov ebx, 0010b
+ jmp .first_quadrant_found
+
+@@: cmp [akode_data.Angle270], eax
+ jne @f
+ mov ebx, 1100b
+ jmp .first_quadrant_found
+
+@@: jb @f
+ mov ebx, 0100b
+ jmp .first_quadrant_found
+
+@@: mov ebx, 1000b
+
+.first_quadrant_found:
+ sub eax, [akode_data.ProjectionPlane.Size.Width]
+ add eax, 1
+ jns @f
+ add eax, [akode_data.Angle360]
+
+@@:
+ cmp [akode_data.Angle0], eax
+ jne @f
+ mov edx, 1001b
+ jmp .second_quadrant_found
+
+@@: cmp [akode_data.Angle90], eax
+ jne @f
+ mov edx, 0011b
+ jmp .second_quadrant_found
+
+@@: jb @f
+ mov edx, 0001b
+ jmp .second_quadrant_found
+
+@@: cmp [akode_data.Angle180], eax
+ jne @f
+ mov edx, 0110b
+ jmp .second_quadrant_found
+
+@@: jb @f
+ mov edx, 0010b
+ jmp .second_quadrant_found
+
+@@: cmp [akode_data.Angle270], eax
+ jne @f
+ mov edx, 1100b
+ jmp .second_quadrant_found
+
+@@: jb @f
+ mov edx, 0100b
+ jmp .second_quadrant_found
+
+@@: mov edx, 1000b
+
+.second_quadrant_found:
+ mov eax, ebx
+ and eax, edx
+ jnz @f
+ mov eax, ebx
+ or eax, edx
+
+@@:
+ mov [quadrants], eax
+
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+
+ mov eax, [akode_data.Camera.Position.X]
+ shr eax, cl
+ mov [camera_cell_x], eax
+
+ mov eax, [akode_data.Camera.Position.Y]
+ shr eax, cl
+ mov [camera_cell_y], eax
+
+ mov esi, [akode_data.CurrentLevelObjectsPtr]
+ xor eax, eax
+ mov [object_count], eax
+ mov ecx, [akode_data.CurrentLevel.ObjectCount]
+
+.calc_distance_to_objects_loop:
+ mov eax, [esi + akode.Object.Visible]
+ test eax, eax
+ jz .next_object
+
+ mov ebx, [esi + akode.Object.Position.X]
+ mov edx, [esi + akode.Object.Position.Y]
+
+ cmp ebx, [camera_cell_x]
+ jne @f
+ mov [x_quadrants], 1111b
+ jmp .x_quadrants_found
+
+@@: jb @f
+ mov [x_quadrants], 1001b
+ jmp .x_quadrants_found
+
+@@: mov [x_quadrants], 0110b
+
+.x_quadrants_found:
+ cmp edx, [camera_cell_y]
+ jne @f
+ mov [y_quadrants], 1111b
+ jmp .y_quadrants_found
+
+@@: jb @f
+ mov [y_quadrants], 1100b
+ jmp .y_quadrants_found
+
+@@: mov [y_quadrants], 0011b
+
+.y_quadrants_found:
+ mov eax, [x_quadrants]
+ and eax, [y_quadrants]
+ cmp eax, 0Fh
+ je .next_object
+
+ test eax, [quadrants]
+ jz .next_object
+
+ push ecx
+ mov ecx, [akode_data.BlockWidthPowerOf2]
+ shl ebx, cl
+ shl edx, cl
+ pop ecx
+ mov eax, [akode_data.BlockSize.Width]
+ shr eax, 1
+ add ebx, eax
+ add edx, eax
+
+ sub ebx, [akode_data.Camera.Position.X]
+ neg edx
+ add edx, [akode_data.Camera.Position.Y]
+
+ push ebx
+ push edx
+ fild dword [esp]
+ fild dword [esp + 4]
+ fpatan
+ add esp, 8
+
+ imul ebx, ebx
+ imul edx, edx
+ add ebx, edx
+
+ push ebx
+ fild dword [esp]
+ fsqrt
+ add esp, 4
+ fistp [esi + akode.Object.Distance]
+
+ mov eax, [esi + akode.Object.Distance]
+ mov [esi + akode.Object.ShadingDistance], eax
+
+ fldpi
+ fldpi
+ faddp
+ fidivr [akode_data.Angle360] ; Angle360 / 2 * pi
+ fmulp
+ fistp [esi + akode.Object.Angle]
+
+ mov eax, [esi + akode.Object.Angle]
+ test eax, eax
+ jns @f
+ add eax, [akode_data.Angle360]
+ mov [esi + akode.Object.Angle], eax
+
+@@:
+ push esi
+ add [object_count], 1
+
+.next_object:
+ add esi, sizeof.akode.Object
+ sub ecx, 1
+ jnz .calc_distance_to_objects_loop
+
+ mov eax, [object_count]
+ test eax, eax
+ jz .exit
+
+ mov ebx, esp
+ stdcall akode._.sort_objects, ebx, eax
+
+ mov ecx, [object_count]
+
+.draw_objects_loop:
+ pop esi
+
+ mov eax, [esi + akode.Object.Angle]
+ sub eax, [akode_data.Camera.Direction]
+ mov ebx, eax
+ jns @f
+ add eax, [akode_data.Angle360]
+ neg ebx
+
+@@:
+ cmp [akode_data.Angle90], eax
+ je .draw_next_object
+ cmp [akode_data.Angle270], eax
+ je .draw_next_object
+
+ mov edi, [akode_data.TrigonometricTablePtr]
+
+ shl eax, 5
+ fld qword [edi + eax + 16]
+ fimul [akode_data.CameraToPlaneDistance]
+ fistp [object_screen_x_delta]
+
+ shl ebx, 5
+ fld qword [edi + ebx + 8]
+ fabs
+ fimul [esi + akode.Object.Distance]
+ fist [esi + akode.Object.Distance]
+ fidivr [akode_data.WallHeightDividend]
+ fistp [object_screen_height]
+ ;fimul [akode_data.BlockSize.Width]
+ ;fidiv [akode_data.BlockSize.Height]
+ ;fistp [object_screen_width]
+
+ mov ebx, [akode_data.ProjectionPlane.MidY]
+ mov eax, [object_screen_height]
+ mov edx, eax
+ shr edx, 1
+ sub ebx, edx
+ mov [object_screen_y], ebx
+
+ mul [akode_data.BlockSize.Width]
+ div [akode_data.BlockSize.Height]
+ mov [object_screen_width], eax
+
+ mov ebx, [akode_data.ProjectionPlane.Size.Width]
+ shr ebx, 1
+ sub ebx, [object_screen_x_delta]
+ shr eax, 1
+ sub ebx, eax
+ mov [object_screen_x], ebx
+
+ push ecx
+ akode._.draw_object
+ pop ecx
+
+.draw_next_object:
+ sub ecx, 1
+ jnz .draw_objects_loop
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc akode._.sort_objects objects_ptr, object_count
+ mov edx, [object_count]
+ mov ecx, 1
+
+ cmp edx, ecx
+ jbe .exit
+
+ mov esi, [objects_ptr]
+
+.sort_loop:
+ mov ebx, ecx
+ mov edi, [esi + ebx * 4]
+
+@@:
+ test ebx, ebx
+ jz .insert
+
+ mov eax, [esi + ebx * 4 - 4]
+ mov eax, [eax + akode.Object.Distance]
+ cmp [edi + akode.Object.Distance], eax
+ jna .insert
+
+ mov eax, [esi + ebx * 4 - 4]
+ mov [esi + ebx * 4], eax
+ sub ebx, 1
+ jmp @b
+
+.insert:
+ mov [esi + ebx * 4], edi
+
+ add ecx, 1
+ cmp edx, ecx
+ jne .sort_loop
+
+.exit:
+ ret
+endp
+
+;void insertionsort()
+; {
+; int i, j, t;
+; for (i=1; i0 && a[j-1]>t)
+; {
+; a[j]=a[j-1];
+; j--;
+; }
+; a[j]=t;
+; }
+; }
+; ============================================================================ ;
+
+; ============================================================================ ;
+; > ebx = width of image (in px) ;
+; > edx = height of image (in px) ;
+; > esi = pointer to source block ;
+; > edi = pointer to destination block ;
+; ============================================================================ ;
+macro akode._.transpose16x16
+{
+ local .loop1, .loop2
+
+ push ebx
+ push edx
+
+ mov ebx, 15
+
+.loop1:
+ mov ecx, 15
+
+.loop2:
+ mov eax, ecx
+ imul eax, [esp]
+ add eax, ebx
+ mov eax, [esi + eax * 4]
+
+ mov edx, ebx
+ imul edx, [esp + 4]
+ add edx, ecx
+ mov [edi + edx * 4], eax
+
+ sub ecx, 1
+ jns .loop2
+
+ sub ebx, 1
+ jns .loop1
+
+ pop edx
+ pop ebx
+}
+
+; ============================================================================ ;
+; > ebx = width of image / 2 (in px) ;
+; > edx = height of image (in px) ;
+; > esi = pointer to source block ;
+; > edi = pointer to destination block ;
+; ============================================================================ ;
+macro akode._.transpose16x16to8x8
+{
+ local .loop1, .loop2
+
+ push ebx
+
+ mov ebx, 7
+
+.loop1:
+ mov ecx, 7
+
+.loop2:
+ mov eax, ecx
+ imul eax, edx
+ add eax, ebx
+ lea eax, [esi + eax * 8]
+
+ movq mm0, [eax]
+ pavgb mm0, [eax + edx * 4]
+ movq mm1, mm0
+ psrlq mm0, 32
+ pavgb mm1, mm0
+
+ mov eax, ebx
+ imul eax, [esp]
+ add eax, ecx
+ movd [edi + eax * 4], mm1
+
+ sub ecx, 1
+ jns .loop2
+
+ sub ebx, 1
+ jns .loop1
+
+ pop ebx
+}
+
+; ============================================================================ ;
+proc akode.get_image uses eax ebx ecx edx esi edi, buffer_ptr, downscale_factor_pow2
+ mov esi, [akode_data.ImageBufferPtr]
+ mov edi, [buffer_ptr]
+ mov ebx, [akode_data.ProjectionPlane.Size.Width]
+ mov edx, [akode_data.ProjectionPlane.Size.Height]
+ mov eax, [downscale_factor_pow2]
+
+ mov ecx, [akode_data.OptimizedGetImage]
+ ;xor ecx, ecx
+ test ecx, ecx
+ jz .no_optimization
+
+; optimized
+ test edi, 0Fh
+ jz @f
+ DEBUGF DEBUG_INFO, 'akode.get_image: buffer_ptr is not aligned by 16\n'
+@@:
+
+ test eax, eax
+ jnz .downscale2_optimized
+
+; no downscale optimized
+ xor eax, eax
+
+.loop1:
+ xor ecx, ecx
+
+.loop2:
+; mov esi, ecx
+; imul esi, edx
+; add esi, eax
+; shl esi, 2
+; add esi, [akode_data.ImageBufferPtr]
+;
+; mov edi, eax
+; imul edi, ebx
+; add edi, ecx
+; shl edi, 2
+; add edi, [buffer_ptr]
+
+ mov esi, ecx
+ mov edi, eax
+
+ imul esi, edx
+ imul edi, ebx
+
+ add esi, eax
+ add edi, ecx
+
+ shl esi, 2
+ shl edi, 2
+
+ add esi, [akode_data.ImageBufferPtr]
+ add edi, [buffer_ptr]
+
+ push eax
+ push ecx
+
+ akode._.transpose16x16
+
+ pop ecx
+ pop eax
+
+ add ecx, 16
+ cmp ecx, ebx
+ jne .loop2
+
+ add eax, 16
+ cmp eax, edx
+ jne .loop1
+
+ jmp .exit
+
+.downscale2_optimized:
+ shr ebx, 1
+
+ xor eax, eax
+
+.loop3:
+ xor ecx, ecx
+
+.loop4:
+; mov esi, ecx
+; imul esi, edx
+; add esi, eax
+; shl esi, 2
+; add esi, [akode_data.ImageBufferPtr]
+;
+; mov edi, eax
+; imul edi, ebx
+; add edi, ecx
+; shl edi, 1
+; add edi, [buffer_ptr]
+
+ mov esi, ecx
+ mov edi, eax
+
+ imul esi, edx
+ imul edi, ebx
+
+ add esi, eax
+ add edi, ecx
+
+ shl esi, 2
+ shl edi, 1
+
+ add esi, [akode_data.ImageBufferPtr]
+ add edi, [buffer_ptr]
+
+ push eax
+ push ecx
+
+ akode._.transpose16x16to8x8
+
+ pop ecx
+ pop eax
+
+ add ecx, 16
+ cmp [akode_data.ProjectionPlane.Size.Width], ecx
+ jne .loop4
+
+ add eax, 16
+ cmp eax, edx
+ jne .loop3
+
+ emms
+
+ jmp .exit
+
+.no_optimization:
+ test eax, eax
+ jnz .downscale2_no_optimization
+
+; no downscale
+ mov ecx, ebx
+ shl ebx, 2
+@@:
+ mov eax, [esi]
+ mov [edi], eax
+ add esi, 4
+ add edi, ebx
+
+ sub edx, 1
+ jnz @b
+
+ mov edx, [akode_data.ProjectionPlane.Size.Height]
+ mov edi, [buffer_ptr]
+ add edi, 4
+ mov [buffer_ptr], edi
+
+ sub ecx, 1
+ jnz @b
+
+ jmp .exit
+
+.downscale2_no_optimization:
+ mov eax, edx
+ shr edx, 1
+ shl eax, 2
+
+ shr ebx, 1
+ mov ecx, ebx
+ shl ecx, 2
+@@:
+ movq mm0, [esi]
+ pavgb mm0, [esi + eax]
+ movq mm1, mm0
+ psrlq mm0, 32
+ pavgb mm1, mm0
+ movd [edi], mm1
+
+ add esi, 8
+ add edi, ecx
+
+ sub edx, 1
+ jnz @b
+
+ mov edx, [akode_data.ProjectionPlane.Size.Height]
+ shr edx, 1
+ add esi, eax
+ mov edi, [buffer_ptr]
+ add edi, 4
+ mov [buffer_ptr], edi
+
+ sub ebx, 1
+ jnz @b
+
+ emms
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
\ No newline at end of file
diff --git a/programs/games/Dungeons/AKODE/data.inc b/programs/games/Dungeons/AKODE/data.inc
new file mode 100644
index 0000000000..f5ca481ad7
--- /dev/null
+++ b/programs/games/Dungeons/AKODE/data.inc
@@ -0,0 +1,19 @@
+; ================================== Data ==================================== ;
+
+align 4
+akode_data.Angle0 dd 0
+
+akode_data.ImageBufferPtr dd 0
+akode_data.TrigonometricTablePtr dd 0
+akode_data.BlockWidthTanTablePtr dd 0
+
+akode_data.DepthBufferPtr dd 0
+
+akode_data.CurrentLevelGridPtr dd 0
+akode_data.CurrentLevelObjectsPtr dd 0
+akode_data.CurrentLevelAddTexturesPtr dd 0
+
+akode_data.ShadingTablePtr dd 0
+
+akode_data.LevelLoadCallback dd 0
+akode_data.ActionCallback dd 0
\ No newline at end of file
diff --git a/programs/games/Dungeons/AKODE/datadef.inc b/programs/games/Dungeons/AKODE/datadef.inc
new file mode 100644
index 0000000000..d6b0a4144f
--- /dev/null
+++ b/programs/games/Dungeons/AKODE/datadef.inc
@@ -0,0 +1,127 @@
+; ============================ Data definitions ============================== ;
+
+struct akode.Point
+ X dd ?
+ Y dd ?
+ends
+
+struct akode.Point3D akode.Point
+ Z dd ?
+ends
+
+struct akode.Size
+ Width dd ?
+ Height dd ?
+ends
+
+struct akode.Rect
+ X dd ?
+ Y dd ?
+ Width dd ?
+ Height dd ?
+ends
+
+struct akode.Camera
+ Position akode.Point3D
+ Direction dd ?
+ FieldOfView dd ?
+ends
+
+struct akode.ProjectionPlane
+ MidY dd ?
+ Size akode.Size
+ends
+
+struct akode.CombinedTexture ; combine 2 textures
+ DescPtr1 dd ? ; main texture
+ DescPtr2 dd ? ; 2nd optional texture
+ends
+
+AKODE_TEXTURE_TYPE.IMAGE = 0
+AKODE_TEXTURE_TYPE.COLOR = 1
+
+AKODE_TEXTURE_USAGE.ENVIRONMENT = 0
+AKODE_TEXTURE_USAGE.OBJECT = 1
+
+AKODE_VIRTUAL_SCALE_MODE.TOPLEFT = 00h
+AKODE_VIRTUAL_SCALE_MODE.TOPCENTER = 01h
+AKODE_VIRTUAL_SCALE_MODE.TOPRIGHT = 02h
+AKODE_VIRTUAL_SCALE_MODE.CENTERLEFT = 10h
+AKODE_VIRTUAL_SCALE_MODE.CENTER = 11h
+AKODE_VIRTUAL_SCALE_MODE.CENTERRIGHT = 12h
+AKODE_VIRTUAL_SCALE_MODE.BOTTOMLEFT = 20h
+AKODE_VIRTUAL_SCALE_MODE.BOTTOMCENTER = 21h
+AKODE_VIRTUAL_SCALE_MODE.BOTTOMRIGHT = 22h
+
+struct akode.TextureDesc
+ Type dd ? ; AKODE_TEXTURE_TYPE
+
+ union
+ ImagePathPtr dd ?
+ Color dd ?
+ ends
+
+ ImageDataPtr dd ?
+
+ HasMagicPink db ? ; boolean
+ TileWalls db ? ; boolean
+ Usage db ? ; AKODE_TEXTURE_USAGE
+ VirtualScale db ? ; AKODE_VIRTUAL_SCALE_MODE, not supported yet
+ VirtualSize akode.Size ; (0, 0) for original size, not supported yet
+ends
+
+struct akode.GridCell
+ WallTexture1 akode.CombinedTexture ; 4 combined textures for walls
+ WallTexture2 akode.CombinedTexture
+ WallTexture3 akode.CombinedTexture
+ WallTexture4 akode.CombinedTexture
+
+ FloorTexture akode.CombinedTexture
+ CeilingTexture akode.CombinedTexture
+
+ Passable dd ? ; boolean
+
+ ActionCallback dd ? ; proc callback AKODE_ACTION, cell x, cell y
+ends
+
+struct akode.Object
+ TextureDescPtr dd ?
+ Position akode.Point
+ Visible dd ? ; boolean
+ DisableShading dd ? ; boolean
+ Tag dd ?
+
+ ; for internal use
+ Distance dd ?
+ ShadingDistance dd ?
+ Angle dd ?
+ends
+
+AKODE_DIRECTION.EAST = 0
+AKODE_DIRECTION.NORTH = 1
+AKODE_DIRECTION.WEST = 2
+AKODE_DIRECTION.SOUTH = 3
+
+struct akode.LevelHeader
+ Size akode.Size
+ StartPosition akode.Point
+ StartDirection dd ? ; AKODE_DIRECTION
+ BackgroundColor dd ? ; FF00FF for no background color
+ ShadingColor dd ?
+ ShadingDistance dd ? ; 0 for no shading
+
+ ObjectCount dd ?
+ TextureCount dd ? ; number of additional textures
+
+ InitCallback dd ?
+ DestroyCallback dd ?
+ ActionCallback dd ? ; proc callback AKODE_ACTION, cell x, cell y
+ends
+
+AKODE_LEVEL_LOAD.START = 0
+AKODE_LEVEL_LOAD.END = 1
+AKODE_LEVEL_LOAD.UNLOADED = 2
+
+AKODE_ACTION.CELL_LEAVE = 0
+AKODE_ACTION.CELL_ENTER = 1
+AKODE_ACTION.CUSTOM = 0100h
\ No newline at end of file
diff --git a/programs/games/Dungeons/AKODE/import.inc b/programs/games/Dungeons/AKODE/import.inc
new file mode 100644
index 0000000000..270e15155a
--- /dev/null
+++ b/programs/games/Dungeons/AKODE/import.inc
@@ -0,0 +1,19 @@
+; ================================= Import =================================== ;
+
+library libio, 'libio.obj', \
+ libimg, 'libimg.obj'
+
+import libio, \
+ file_size, 'file_size', \
+ file_open, 'file_open', \
+ file_read, 'file_read', \
+ file_close, 'file_close'
+
+import libimg, \
+ img_decode, 'img_decode', \
+ img_convert, 'img_convert', \
+ img_scale, 'img_scale', \
+ img_flip, 'img_flip', \
+ img_rotate, 'img_rotate', \
+ img_to_rgb, 'img_to_rgb', \
+ img_destroy, 'img_destroy'
\ No newline at end of file
diff --git a/programs/games/Dungeons/AKODE/udata.inc b/programs/games/Dungeons/AKODE/udata.inc
new file mode 100644
index 0000000000..ecdf36144c
--- /dev/null
+++ b/programs/games/Dungeons/AKODE/udata.inc
@@ -0,0 +1,34 @@
+; =========================== Uninitialized data ============================= ;
+
+align 4
+akode_data.BlockSize akode.Size ; first - width and height of the block base, must be power of 2
+ ; second - height of the block
+akode_data.BlockWidthPowerOf2 dd ?
+akode_data.Camera akode.Camera
+akode_data.ProjectionPlane akode.ProjectionPlane
+
+akode_data.CameraToPlaneDistance dd ?
+akode_data.WallHeightDividend dd ?
+akode_data.FloorDistanceDividend dd ?
+akode_data.CeilingDistanceDividend dd ?
+
+akode_data.Angle90 dd ?
+akode_data.Angle180 dd ?
+akode_data.Angle270 dd ?
+akode_data.Angle360 dd ?
+
+akode_data.ShadingColor dd ?
+akode_data.ShadingDistance dd ?
+
+akode_data.CurrentLevel akode.LevelHeader
+
+akode_data.MovementSpeed dd ?
+akode_data.TurningSpeed dd ?
+akode_data.MovementDirection dd ?
+akode_data.TurningDirection dd ?
+akode_data.LastMoveTimestamp dd ?
+akode_data.LastTurnTimestamp dd ?
+
+akode_data.ImageBufferSize dd ?
+
+akode_data.OptimizedGetImage dd ?
\ No newline at end of file
diff --git a/programs/games/Dungeons/Dungeons.asm b/programs/games/Dungeons/Dungeons.asm
new file mode 100644
index 0000000000..c679dabf9c
--- /dev/null
+++ b/programs/games/Dungeons/Dungeons.asm
@@ -0,0 +1,974 @@
+; Copyright (C) 2014 Anton_K
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see .
+
+format binary as 'kex'
+
+__DEBUG__ = 1 ; 0 - disable debug output / 1 - enable debug output
+__DEBUG_LEVEL__ = DEBUG_FINE ; DEBUG_FINE - all debug messages / DEBUG_INFO - info and errors / DEBUG_ERR - only errors
+
+DEBUG_FINE = 0
+DEBUG_INFO = 1
+DEBUG_ERR = 2
+
+include 'macros.inc'
+purge mov, add, sub
+
+; ================================= Header =================================== ;
+MEOS_APP_START
+store dword StartupPath at $ - 4
+
+; ================================ Includes ================================== ;
+include '../../debug-fdo.inc'
+include '../../proc32.inc'
+include '../../dll.inc'
+
+include 'AKODE/AKODE.inc'
+include 'datadef.inc'
+
+; =============================== Entry point ================================ ;
+CODE
+ DEBUGF DEBUG_INFO, 'Started\n'
+
+ mcall 68, 11 ; initialize heap
+ test eax, eax
+ jz .exit_fail_heap_init
+
+ stdcall dll.Load, @IMPORT ; load libraries
+ test eax, eax
+ jnz .exit_fail_load_libs
+
+ mcall 40, MAIN_EVENT_MASK ; used events
+ mcall 66, 1, 1 ; use scancodes
+
+ stdcall draw_window
+
+ mcall 9, ThreadInfoBuffer, -1 ; get real window size
+ mov eax, [ebx + process_information.client_box.width]
+ inc eax
+ mov [MainWindowWidth], eax
+ mov edx, [ebx + process_information.client_box.height]
+ inc edx
+ mov [MainWindowHeight], edx
+ DEBUGF DEBUG_FINE, 'Window width: %u\nWindow height: %u\n', eax, edx
+
+ mov ecx, eax
+ shl ecx, 2
+ imul ecx, edx ; ecx = width * 4 * height
+
+ sub edx, HUD_PANEL_HEIGHT
+ mov [WorldViewHeight], edx
+
+if FSAA
+ shl eax, FSAA
+ shl edx, FSAA
+end if
+
+ stdcall akode.init, eax, edx, FIELD_OF_VIEW, BLOCK_BASE_SIZE, BLOCK_HEIGHT
+ test eax, eax
+ jz .exit_fail_akode_init
+
+ mcall 68, 12 ; alloc ecx bytes for image buffer
+ test eax, eax
+ jz .exit_fail_alloc
+
+ mov [ImageBufferPtr], eax
+ push eax
+
+ stdcall set_startup_path
+
+ stdcall load_hud_images
+ test eax, eax
+ jnz @f
+ DEBUGF DEBUG_ERR, 'Failed to load HUD images\n'
+ jmp .exit_fail_load_hud
+
+@@:
+ cld
+ xor eax, eax
+ mov edi, PressedKeys
+ mov ecx, 128 * 2
+ rep stosd
+
+ mov edi, Inventory
+ mov ecx, INVENTORY_SIZE * 2
+ rep stosd
+
+ stdcall akode.set_callbacks, level_load_callback, action_callback
+
+ stdcall akode.load_level, levels.level1
+
+if FULLSCREEN
+ stdcall hide_cursor
+ push eax
+end if
+
+ stdcall main_loop
+
+if FULLSCREEN
+ pop ecx
+ test ecx, ecx
+ jz @f
+ mcall 37, 6 ; delete cursor
+@@:
+end if
+
+.exit_fail_load_hud:
+ stdcall free_hud_images
+
+ pop ecx
+ mcall 68, 13 ; free image buffer
+
+ jmp .exit
+
+.exit_fail_heap_init:
+ DEBUGF DEBUG_ERR, 'Heap initialization failed\n'
+ jmp .exit
+
+.exit_fail_load_libs:
+ DEBUGF DEBUG_ERR, 'Failed to load libraries\n'
+ jmp .exit
+
+.exit_fail_akode_init:
+ DEBUGF DEBUG_ERR, 'AKODE initialization failed\n'
+ jmp .exit
+
+.exit_fail_alloc:
+ DEBUGF DEBUG_ERR, 'Memory allocation for image buffer failed\n'
+ ;jmp .exit
+
+.exit:
+ stdcall akode.cleanup
+ DEBUGF DEBUG_INFO, 'Exiting\n'
+
+ xor eax, eax
+ dec eax
+ mcall ; kill this thread
+
+; ============================================================================ ;
+proc set_startup_path
+ cld
+ mov esi, StartupPath
+ mov ecx, esi
+
+@@:
+ lodsb
+ test al, al
+ jnz @b
+
+ sub esi, 2
+ std
+
+@@:
+ lodsb
+ cmp al, '/'
+ jne @b
+
+ mov [esi + 1], byte 0
+
+ mcall 30, 1 ; set current directory
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 0 - fail ;
+; ============================================================================ ;
+proc load_hud_images
+ mov ebx, [MainWindowWidth]
+ mov ecx, [MainWindowHeight]
+ xor edx, edx
+
+ stdcall akode.load_and_scale_image, LevelLoadingImageFile, ebx, ecx, edx
+ test eax, eax
+ jz .exit
+
+ mov [LevelLoadingImagePtr], eax
+
+ stdcall akode.load_and_scale_image, DeathImageFile, ebx, ecx, edx
+ test eax, eax
+ jz .exit
+
+ mov [DeathImagePtr], eax
+
+ stdcall akode.load_and_scale_image, EndImageFile, ebx, ecx, edx
+ test eax, eax
+ jz .exit
+
+ mov [EndImagePtr], eax
+
+ stdcall akode.load_and_scale_image, HudPanelImageFile, ebx, HUD_PANEL_HEIGHT, edx
+ test eax, eax
+ jz .exit
+
+ mov [HudPanelImagePtr], eax
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc free_hud_images
+ xor edx, edx
+ mov ebx, 13
+
+ mov ecx, [LevelLoadingImagePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68 ; free
+ mov [LevelLoadingImagePtr], edx
+@@:
+ mov ecx, [HudPanelImagePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [HudPanelImagePtr], edx
+@@:
+ mov ecx, [DeathImagePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [DeathImagePtr], edx
+@@:
+ mov ecx, [EndImagePtr]
+ test ecx, ecx
+ jz @f
+ mcall 68
+ mov [EndImagePtr], edx
+@@:
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = cursor handle / 0 - fail ;
+; ============================================================================ ;
+proc hide_cursor
+ mcall 68, 12, 32 * 32 * 4
+ test eax, eax
+ jz .exit
+
+ mov edi, eax
+ xor ebx, ebx
+ shr ecx, 2
+@@: mov [eax], ebx
+ add eax, 4
+ loop @b
+
+ mcall 37, 4, edi, 2 ; load cursor
+ mov ecx, eax
+ inc ebx
+ mcall 37 ; set cursor
+
+ xchg edi, ecx
+
+ mcall 68, 13
+
+ mov eax, edi
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_image uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
+ mov ebx, [image_ptr]
+ mpack ecx, [width], [height]
+ mpack edx, [x], [y]
+ mov esi, 32
+ xor edi, edi
+ xchg edi, ebp
+ mcall 65
+ mov ebp, edi
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_image_to_buffer uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
+ cld
+ mov esi, [image_ptr]
+ mov edi, [ImageBufferPtr]
+ mov ebx, [MainWindowWidth]
+ mov eax, [y]
+ mul ebx
+ add eax, [x]
+ lea edi, [edi + eax * 4]
+
+ sub ebx, [width]
+ shl ebx, 2
+ mov edx, [height]
+
+@@:
+ mov ecx, [width]
+ rep movsd
+ add edi, ebx
+
+ sub edx, 1
+ jnz @b
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_image_with_transparency_to_buffer uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
+ mov esi, [image_ptr]
+ mov edi, [ImageBufferPtr]
+ mov ebx, [MainWindowWidth]
+ mov eax, [y]
+ mul ebx
+ add eax, [x]
+ lea edi, [edi + eax * 4]
+
+ sub ebx, [width]
+ shl ebx, 2
+ mov edx, [height]
+
+.y_draw_loop:
+ mov ecx, [width]
+
+.x_draw_loop:
+ mov eax, [esi]
+ cmp eax, 0FF00FFh
+ je @f
+ mov [edi], eax
+@@:
+ add esi, 4
+ add edi, 4
+
+ sub ecx, 1
+ jnz .x_draw_loop
+
+ add edi, ebx
+
+ sub edx, 1
+ jnz .y_draw_loop
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_window
+ mcall 12, 1 ; start drawing
+
+if FULLSCREEN
+ mov ebx, 0FFFFh
+ mov ecx, 0FFFFh
+else
+ mcall 48, 4 ; eax - skin height
+
+ mpack ebx, MAIN_WINDOW_X, MAIN_WINDOW_WIDTH + 9
+ mpack ecx, MAIN_WINDOW_Y, MAIN_WINDOW_HEIGHT + 4
+ add ecx, eax
+end if
+ mov edx, MAIN_WINDOW_STYLE
+ mov esi, MAIN_WINDOW_STYLE2
+ mov edi, MAIN_WINDOW_TITLE
+ xor eax, eax
+ mcall ; draw window
+
+ mcall 12, 2 ; end drawing
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_world
+ mov ebx, [ImageBufferPtr]
+ ;test ebx, ebx
+ ;jz @f
+
+ stdcall akode.render
+ stdcall akode.get_image, ebx, FSAA
+
+ mpack ecx, [MainWindowWidth], [WorldViewHeight]
+ xor edx, edx
+ mov esi, 32
+ xor edi, edi
+ xchg edi, ebp
+ mcall 65
+ mov ebp, edi
+
+;@@:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_hud
+ mov eax, [HudPanelNeedsRedraw]
+ test eax, eax
+ jz .exit
+
+ xor eax, eax
+ mov [HudPanelNeedsRedraw], eax
+
+ stdcall draw_image_to_buffer, [HudPanelImagePtr], eax, [WorldViewHeight], [MainWindowWidth], HUD_PANEL_HEIGHT
+
+ mov esi, Inventory + 4
+ mov ebx, INVENTORY_Y
+ add ebx, [WorldViewHeight]
+ mov edx, 2
+
+.y_inventory_loop:
+ mov eax, INVENTORY_X
+ mov ecx, INVENTORY_SIZE / 2
+
+.x_inventory_loop:
+ mov edi, [esi]
+ add esi, 8
+ test edi, edi
+ jz @f
+ stdcall draw_image_with_transparency_to_buffer, edi, eax, ebx, OBJECT_IMAGE_WIDTH, OBJECT_IMAGE_HEIGHT
+
+@@:
+ add eax, OBJECT_IMAGE_WIDTH + INVENTORY_PADDING_X
+
+ sub ecx, 1
+ jnz .x_inventory_loop
+
+ add ebx, OBJECT_IMAGE_HEIGHT + INVENTORY_PADDING_Y
+
+ sub edx, 1
+ jnz .y_inventory_loop
+
+ mpack ecx, [MainWindowWidth], HUD_PANEL_HEIGHT
+ mov edx, [WorldViewHeight]
+ mov ebx, [ImageBufferPtr]
+ mov eax, [MainWindowWidth]
+ imul eax, edx
+ lea ebx, [ebx + eax * 4]
+ mov esi, 32
+ xor edi, edi
+ xchg edi, ebp
+ mcall 65
+ mov ebp, edi
+
+ jmp draw_game_message
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc draw_game_message
+ mov esi, [GameMessage]
+ test esi, esi
+ jz .exit
+
+ mpack ebx, GAME_MESSAGE_X, GAME_MESSAGE_Y
+ add ebx, [WorldViewHeight]
+ mov ecx, GAME_MESSAGE_COLOR or (80h shl 24)
+
+.draw_strings_loop:
+ mov edx, esi
+
+@@:
+ mov al, [esi]
+ add esi, 1
+
+ test al, al
+ jz .draw_last_string
+ cmp al, 0Ah
+ jne @b
+
+ mov [esi - 1], byte 0
+
+ mcall 4
+
+ mov [esi - 1], byte 0Ah
+ add ebx, 10
+ jmp .draw_strings_loop
+
+.draw_last_string:
+ mcall 4
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc main_loop
+ locals
+ frame_count dd 0
+ last_timestamp dd 0
+ endl
+
+.main_loop:
+ mcall 26, 9 ; get timestamp
+ mov ebx, eax
+ sub ebx, [last_timestamp]
+ cmp ebx, 100
+ jb @f
+
+ mov [last_timestamp], eax
+ imul eax, [frame_count], 100
+ xor edx, edx
+ mov [frame_count], edx
+ div ebx ; eax - fps
+
+ DEBUGF DEBUG_FINE, 'FPS: %u\n', eax
+
+@@:
+ mcall 11 ; check events
+
+ test eax, eax
+ jz .idle
+
+ dec eax
+ jz .redraw
+
+ dec eax
+ jz .key
+
+ dec eax
+ jz .button
+
+ sub eax, 3
+ jz .mouse
+
+ jmp .idle
+
+.redraw:
+ stdcall draw_window
+ mov [HudPanelNeedsRedraw], 1
+
+.idle:
+ mov eax, [GameStatus]
+ test eax, eax
+ jnz @f
+
+ stdcall akode.process
+
+ mov eax, [GameStatus]
+ test eax, eax
+ jnz @f
+
+ stdcall draw_hud
+ stdcall draw_world
+
+ add [frame_count], 1
+ jmp .main_loop
+
+@@: cmp eax, GAME_STATUS.LEVEL_LOAD_FAILED
+ jne @f
+ jmp .exit
+
+@@: cmp eax, GAME_STATUS.DEAD
+ jne @f
+ stdcall draw_image, [DeathImagePtr], 0, 0, [MainWindowWidth], [MainWindowHeight]
+ jmp .main_loop
+
+@@: cmp eax, GAME_STATUS.END
+ jne @f
+ stdcall draw_image, [EndImagePtr], 0, 0, [MainWindowWidth], [MainWindowHeight]
+@@:
+ jmp .main_loop
+
+.key:
+ stdcall get_key
+ test eax, eax
+ jz .idle
+
+ cmp eax, 001h ; Esc
+ je .exit
+
+ cmp eax, 039h ; Space
+ je .space_pressed
+ cmp eax, 002h ; 1
+ jb @f
+ cmp eax, 00Bh ; 0
+ ja @f
+
+ ; 0..9 pressed
+ sub eax, 002h
+ mov eax, dword [Inventory + eax * 8]
+ test eax, eax
+ jz @f
+ shl eax, 16
+ push eax
+
+ stdcall check_key, 02Ah ; left Shift
+ test eax, eax
+ jnz .shift_pressed
+
+ stdcall check_key, 036h ; right Shift
+ test eax, eax
+ jnz .shift_pressed
+
+ pop eax
+ or eax, ACTION.USE_OBJECT
+ stdcall akode.action, eax
+ jmp @f
+
+.shift_pressed:
+ pop eax
+ or eax, ACTION.LOOK_AT_OBJECT
+ stdcall akode.action, eax
+ jmp @f
+
+.space_pressed:
+ stdcall check_key, 02Ah ; left Shift
+ test eax, eax
+ jnz .shift_pressed2
+
+ stdcall check_key, 036h ; right Shift
+ test eax, eax
+ jnz .shift_pressed2
+
+ stdcall akode.action, ACTION.DO_SOMETHING
+ jmp @f
+
+.shift_pressed2:
+ stdcall akode.action, ACTION.LOOK_AROUND
+
+@@:
+ xor esi, esi
+ dec esi
+
+ stdcall check_key, 0E048h ; ^
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.NORTH
+ jmp .set_moving_direction
+
+@@: stdcall check_key, 0E050h ; v
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.SOUTH
+ jmp .set_moving_direction
+
+@@: stdcall check_key, 011h ; W
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.NORTH
+ jmp .set_moving_direction
+
+@@: stdcall check_key, 01Fh ; S
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.SOUTH
+ ;jmp .set_moving_direction
+
+@@:
+
+.set_moving_direction:
+ test esi, esi
+ js @f
+ stdcall akode.start_moving, esi
+ jmp .turn
+
+@@:
+ stdcall akode.stop_moving
+
+.turn:
+ xor esi, esi
+ dec esi
+
+ stdcall check_key, 0E04Bh ; <-
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.WEST
+ jmp .set_turning_direction
+
+@@: stdcall check_key, 0E04Dh ; ->
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.EAST
+ jmp .set_turning_direction
+
+@@: stdcall check_key, 01Eh ; A
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.WEST
+ jmp .set_turning_direction
+
+@@: stdcall check_key, 020h ; D
+ test eax, eax
+ jz @f
+ mov esi, AKODE_DIRECTION.EAST
+ ;jmp .set_turning_direction
+
+@@:
+
+.set_turning_direction:
+ test esi, esi
+ js @f
+ stdcall akode.start_turning, esi
+ jmp .key
+
+@@:
+ stdcall akode.stop_turning
+ jmp .key
+
+.mouse:
+
+ jmp .idle
+
+.button:
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = key scancode / 0 - no keys ;
+; ============================================================================ ;
+proc get_key
+ mcall 2 ; get key scancode
+
+ test al, al
+ jz @f
+ xor eax, eax
+ jmp .exit
+
+@@:
+ shr eax, 8
+
+ cmp eax, 0E1h
+ jne @f
+ mcall 2
+ mcall 2
+ xor eax, eax
+ jmp .exit
+
+@@:
+ xor ebx, ebx
+ mov ecx, eax
+
+ cmp eax, 0E0h
+ jne @f
+ mcall 2
+ shr eax, 8
+ mov ecx, eax
+ or eax, 0E000h
+ mov ebx, 128
+
+@@:
+ test ecx, 80h
+ jnz .key_up
+
+ ; key down
+ add ebx, ecx
+ lea ebx, [PressedKeys + ebx * 4]
+
+ mov edx, [ebx]
+ test edx, edx
+ jz @f
+
+ xor eax, eax
+ jmp .exit
+
+@@:
+ inc edx
+ mov [ebx], edx
+ jmp .exit
+
+.key_up:
+ and ecx, 7Fh
+ add ebx, ecx
+ xor edx, edx
+ mov [PressedKeys + ebx * 4], edx
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = 1 - key pressed / 0 - not pressed ;
+; ============================================================================ ;
+proc check_key scancode
+ mov eax, [scancode]
+ mov ecx, eax
+ shr eax, 8
+ and ecx, 7Fh
+ xor ebx, ebx
+
+ cmp eax, 0E0h
+ jne @f
+ mov ebx, 128
+
+@@:
+ add ebx, ecx
+ mov eax, [PressedKeys + ebx * 4]
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc level_load_callback uses eax ebx ecx, load_action, action_result
+ mov eax, [load_action]
+ mov ebx, [action_result]
+ xor ecx, ecx
+
+ cmp eax, AKODE_LEVEL_LOAD.START
+ jne @f
+ stdcall draw_image, [LevelLoadingImagePtr], ecx, ecx, [MainWindowWidth], [MainWindowHeight]
+
+ cmp ebx, -1
+ je .level_load_failed
+
+ mov [GameMessage], ebx
+ inc ecx
+ mov [HudPanelNeedsRedraw], ecx
+ jmp .exit
+
+@@: cmp eax, AKODE_LEVEL_LOAD.END
+ jne @f
+ DEBUGF DEBUG_INFO, 'Level load result: %u\n', ebx
+
+ test ebx, ebx
+ jnz .exit
+
+.level_load_failed:
+ DEBUGF DEBUG_ERR, 'Failed to load level\n'
+ mov [GameStatus], GAME_STATUS.LEVEL_LOAD_FAILED
+ ;jmp .exit
+
+@@:
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc action_callback action, cell_x, cell_y, action_result
+ m2m [GameMessage], [action_result]
+ mov [HudPanelNeedsRedraw], 1
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc add_object_to_inventory uses eax ecx edi, object_id, object_image_ptr
+ mov edi, Inventory
+ mov ecx, INVENTORY_SIZE
+
+.inventory_loop:
+ mov eax, [edi]
+ test eax, eax
+ jnz @f
+
+ mov eax, [object_id]
+ mov [edi], eax
+ mov eax, [object_image_ptr]
+ mov [edi + 4], eax
+ mov [HudPanelNeedsRedraw], ecx
+ jmp .exit
+
+@@:
+ add edi, 8
+
+ sub ecx, 1
+ jnz .inventory_loop
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc remove_object_from_inventory uses eax ecx esi, object_id
+ mov eax, [object_id]
+ mov esi, Inventory
+ mov ecx, INVENTORY_SIZE
+
+.inventory_loop:
+ cmp [esi], eax
+ jne @f
+
+ xor eax, eax
+ mov [esi], eax
+ mov [esi + 4], eax
+ mov [HudPanelNeedsRedraw], ecx
+ jmp .exit
+
+@@:
+ add esi, 8
+
+ sub ecx, 1
+ jnz .inventory_loop
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+; < eax = pointer to image data / 0 - fail ;
+; ============================================================================ ;
+proc load_object_image image_path_ptr
+ stdcall akode.load_and_scale_image, [image_path_ptr], OBJECT_IMAGE_WIDTH, OBJECT_IMAGE_HEIGHT, 0
+
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc free_object_image uses eax ebx ecx, image_ptr
+ mov ecx, [image_ptr]
+ test ecx, ecx
+ jz .exit
+
+ mcall 68, 13
+
+.exit:
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc player_death
+ mov [GameStatus], GAME_STATUS.DEAD
+ ret
+endp
+; ============================================================================ ;
+
+; ============================================================================ ;
+proc game_over
+ mov [GameStatus], GAME_STATUS.END
+ ret
+endp
+; ============================================================================ ;
+
+; ============================ Initialized data ============================== ;
+DATA
+ include 'data.inc'
+
+ ; for debug-fdo
+ include_debug_strings
+
+ align 4
+ @IMPORT:
+ include 'import.inc'
+
+; =========================== Uninitialized data ============================= ;
+UDATA
+ include 'udata.inc'
+
+; ================================= The End ================================== ;
+MEOS_APP_END
\ No newline at end of file
diff --git a/programs/games/Dungeons/Resources/Levels/1.inc b/programs/games/Dungeons/Resources/Levels/1.inc
new file mode 100644
index 0000000000..4f604b0eb0
--- /dev/null
+++ b/programs/games/Dungeons/Resources/Levels/1.inc
@@ -0,0 +1,447 @@
+
+levels.level1 akode.LevelHeader , <8, 1>, AKODE_DIRECTION.SOUTH, 0FF00FFh, 000000h, BLOCK_BASE_SIZE * 5, 6, 1, level1.init, 0, level1.action
+
+level1:
+
+.Width = 24
+.Height = 25
+
+W equ level1.Wall
+w equ level1.OtherWall
+_ equ level1.PassableArea
+0 equ level1.NotPassableArea
+% equ level1.WallWithCrazyWoodDoor
+I equ level1.IronDoor
+L equ level1.WallWithLever1
+q equ level1.WallWithLever2
+v equ level1.WallWithLever3
+e equ level1.WallWithLever4
+c equ level1.ColumnArea
+o equ level1.OgreArea
+P equ level1.PortalArea
+
+.Grid:
+dd W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W
+dd W,_,_,W,W,_,_,W,_,W,_,W,_,_,_,_,W,W,W,W,W,W,W,W
+dd W,_,_,W,W,_,_,W,_,W,_,W,_,_,_,_,_,_,_,_,_,_,_,W
+dd W,_,_,_,_,_,_,W,_,W,_,W,_,_,_,_,_,_,_,_,_,_,_,W
+dd W,W,W,_,_,W,W,W,_,W,L,W,_,_,_,W,W,W,_,_,_,W,W,W
+dd W,_,_,_,_,_,_,W,_,_,_,W,_,_,_,W,0,W,_,_,_,W,0,W
+dd W,_,W,_,W,_,_,W,_,W,W,W,W,W,_,W,c,_,_,_,_,_,c,W
+dd W,_,W,_,W,_,_,W,_,W,W,W,W,W,_,W,_,_,_,c,_,_,_,W
+dd W,W,W,_,W,W,W,W,%,W,W,W,W,W,_,W,_,_,W,0,W,_,_,W
+dd W,_,W,_,W,_,_,_,_,_,_,_,W,W,_,W,_,_,W,W,W,_,_,W
+dd W,_,W,W,W,_,_,_,_,_,_,_,I,_,_,W,_,_,_,_,_,_,_,W
+dd W,_,_,_,W,_,_,_,_,_,_,_,W,W,W,W,W,W,W,W,W,%,W,W
+dd W,_,_,_,W,W,W,W,q,v,e,W,W,_,_,_,_,_,_,_,_,_,_,W
+dd W,_,_,_,_,_,_,_,_,_,_,_,W,_,W,W,W,W,W,W,W,W,W,W
+dd W,_,_,_,W,_,_,_,_,_,_,_,W,o,W,_,_,_,_,_,_,_,_,W
+dd W,_,_,_,W,W,W,W,W,W,W,W,W,0,W,W,W,W,_,_,_,_,_,W
+dd W,_,_,_,W,_,_,_,_,_,_,W,_,_,_,_,_,W,w,w,w,w,w,w
+dd W,_,_,_,_,_,_,_,_,_,_,W,_,_,_,_,_,_,_,_,_,_,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,W,W,W,W,W,_,w,w,_,w,w,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,_,_,_,_,w,_,w,_,_,_,w,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,_,_,_,_,w,_,_,_,P,_,_,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,_,_,_,_,w,_,w,_,_,_,w,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,_,_,_,_,w,_,w,w,_,w,w,_,w
+dd W,_,_,_,_,_,_,_,_,_,_,_,_,_,_,w,_,_,_,_,_,_,_,w
+dd W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,w,w,w,w,w,w,w,w,w
+
+restore W, w, _, 0, %, I, L, q, v, e, c, o, P
+
+.Chest akode.Object textures.Chest, <13, 2>, 1, 0
+.Column1 akode.Object textures.Column1, <16, 5>, 1, 0
+.Column2 akode.Object textures.Column2, <19, 8>, 1, 0
+.Column3 akode.Object textures.Column3, <22, 5>, 1, 0
+.Ogre akode.Object textures.Ogre, <13, 15>, 1, 0
+.Portal akode.Object textures.Portal, <19, 20>, 1, 1
+
+.Column4TextureDescPtr dd textures.Column4
+
+.Wall akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.OtherWall akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.WallWithCrazyWoodDoor akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.IronDoor akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.WallWithLever1 akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.WallWithLever2 akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.WallWithLever3 akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.WallWithLever4 akode.GridCell , \
+ , \
+ , \
+ , \
+ <0, 0>, \
+ <0, 0>, \
+ 0, 0
+
+.PassableArea akode.GridCell <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ , \
+ , \
+ 1, 0
+
+.NotPassableArea akode.GridCell <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ , \
+ , \
+ 0, 0
+
+.ColumnArea akode.GridCell <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ , \
+ , \
+ 1, level1.columns
+
+.OgreArea akode.GridCell <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ , \
+ , \
+ 1, level1.ogre
+
+.PortalArea akode.GridCell <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ <0, 0>, \
+ , \
+ , \
+ 1, level1.portal
+
+.CoinImagePtr dd 0
+.Sun1ImagePtr dd 0
+.Sun2ImagePtr dd 0
+.Sun3ImagePtr dd 0
+
+proc level1.init
+ stdcall load_object_image, .coin_file_path
+ mov [level1.CoinImagePtr], eax
+ stdcall load_object_image, .sun1_file_path
+ mov [level1.Sun1ImagePtr], eax
+ stdcall load_object_image, .sun2_file_path
+ mov [level1.Sun2ImagePtr], eax
+ stdcall load_object_image, .sun3_file_path
+ mov [level1.Sun3ImagePtr], eax
+
+ mov eax, .message
+ ret
+
+.coin_file_path db 'Resources/Textures/Objects/Coin.png', 0
+.sun1_file_path db 'Resources/Textures/Objects/Sun1.png', 0
+.sun2_file_path db 'Resources/Textures/Objects/Sun2.png', 0
+.sun3_file_path db 'Resources/Textures/Objects/Sun3.png', 0
+
+.message langstr0 ru, < \
+'Вы - искатель сокровищ. Тяга к наживе привела вас в ', 10, \
+'лесную пещеру, где, по рассказам селян, пару столетий ', 10, \
+'назад, один правитель, спрятал свою казну. В пещере вы ', 10, \
+'нашли... дверь. Усилие - дверь открыта! Вы вошли внутрь', 10, \
+'и дверь с грохотом захлопнулась. Возможно, это ваш шанс', 10, \
+'найти несметные богатства или погибнуть... ' >
+
+endp
+
+proc level1.action uses ebx ecx edx, action, cell_x, cell_y
+ mov eax, [action]
+ mov ebx, [cell_x]
+ mov ecx, [cell_y]
+
+ mov edx, ecx
+ imul edx, level1.Width
+ add edx, ebx
+ lea edx, [level1.Grid + edx * 4]
+
+ cmp ax, ACTION.DO_SOMETHING
+ jne .check_next_action
+
+ cmp edx, level1.Grid + (7 * level1.Width + 8) * 4
+ jne @f
+ cmp dword [level1.Grid + (8 * level1.Width + 8) * 4], level1.WallWithCrazyWoodDoor
+ jne .exit_no_message
+
+ mov eax, .message_door1_closed
+ jmp .exit
+.message_door1_closed langstr0 ru, < \
+'Заперто. У двери нет ни ручки, ни замочной скважины. ', 10, \
+'Вероятно, где-то должен быть скрытый механизм... ' >
+
+@@: cmp edx, level1.Grid + (10 * level1.Width + 21) * 4
+ jne @f
+ cmp dword [level1.Grid + (11 * level1.Width + 21) * 4], level1.WallWithCrazyWoodDoor
+ jne .exit_no_message
+
+ mov eax, .message_door3_closed
+ jmp .exit
+.message_door3_closed langstr0 ru, < \
+'Такую дверь вы уже видели, и в прошлый раз она так ', 10, \
+'просто не открылась. ' >
+
+@@: cmp edx, level1.Grid + (5 * level1.Width + 10) * 4
+ jne @f
+ cmp dword [level1.Grid + (8 * level1.Width + 8) * 4], level1.WallWithCrazyWoodDoor
+ jne .exit_no_message
+
+ mov [level1.WallWithLever1.WallTexture4.DescPtr2], textures.RedLeverDown
+ mov dword [level1.Grid + (8 * level1.Width + 8) * 4], level1.PassableArea
+
+ mov eax, .message_door1_opens
+ jmp .exit
+.message_door1_opens langstr0 ru, < \
+'Вы слышите звук лебёдки и скрежет шестерёнок. ' >
+
+@@: cmp edx, level1.Grid + (11 * level1.Width + 8) * 4
+ jne @f
+ mov eax, [level1.WallWithLever2.WallTexture2.DescPtr2]
+ mov ebx, [level1.WallWithLever2.WallTexture4.DescPtr2]
+ mov [level1.WallWithLever2.WallTexture2.DescPtr2], ebx
+ mov [level1.WallWithLever2.WallTexture4.DescPtr2], eax
+ jmp .3levers
+
+@@: cmp edx, level1.Grid + (11 * level1.Width + 9) * 4
+ jne @f
+ mov eax, [level1.WallWithLever3.WallTexture2.DescPtr2]
+ mov ebx, [level1.WallWithLever3.WallTexture4.DescPtr2]
+ mov [level1.WallWithLever3.WallTexture2.DescPtr2], ebx
+ mov [level1.WallWithLever3.WallTexture4.DescPtr2], eax
+ jmp .3levers
+
+@@: cmp edx, level1.Grid + (11 * level1.Width + 10) * 4
+ jne @f
+ mov eax, [level1.WallWithLever4.WallTexture2.DescPtr2]
+ mov ebx, [level1.WallWithLever4.WallTexture4.DescPtr2]
+ mov [level1.WallWithLever4.WallTexture2.DescPtr2], ebx
+ mov [level1.WallWithLever4.WallTexture4.DescPtr2], eax
+ jmp .3levers
+
+@@: cmp edx, level1.Grid + (10 * level1.Width + 11) * 4
+ jne @f
+ cmp dword [level1.Grid + (10 * level1.Width + 12) * 4], level1.IronDoor
+ jne .exit_no_message
+ mov eax, .message_door2_closed
+ jmp .exit
+
+@@: jmp .exit_no_message
+
+.check_next_action:
+ cmp ax, AKODE_ACTION.CELL_ENTER
+ jne .exit_no_message
+
+ cmp edx, level1.Grid + (2 * level1.Width + 13) * 4
+ jne @f
+ cmp [level1.Chest.Visible], 1
+ jne .exit_no_message
+
+ stdcall add_object_to_inventory, 4, [level1.CoinImagePtr]
+ stdcall add_object_to_inventory, 1, [level1.Sun1ImagePtr]
+ stdcall add_object_to_inventory, 2, [level1.Sun2ImagePtr]
+ stdcall add_object_to_inventory, 3, [level1.Sun3ImagePtr]
+
+ mov [level1.Chest.Visible], 0
+ mov eax, .message_chest
+ jmp .exit
+.message_chest langstr0 ru, < \
+'Вы нашли сундук с барахлом: 1 золотой монеткой и ', 10, \
+'3-мя монетами с изображением солнца в разных фазах. ' >
+
+@@: cmp edx, level1.Grid + (14 * level1.Width + 13) * 4
+ jne @f
+ cmp [level1.Ogre.Visible], 1
+ jne .exit_no_message
+ mov eax, .message_ogre
+ jmp .exit
+
+.message_ogre langstr0 ru, < \
+'- Стой! Куда это ты? Дальше хода нет. ' >
+
+@@:
+
+.exit_no_message:
+ xor eax, eax
+
+.exit:
+ ret
+
+.3levers:
+ mov eax, [level1.WallWithLever2.WallTexture2.DescPtr2]
+ mov ebx, [level1.WallWithLever3.WallTexture2.DescPtr2]
+ mov ecx, [level1.WallWithLever4.WallTexture2.DescPtr2]
+
+ cmp eax, textures.RedLeverDown
+ jne @f
+ cmp ebx, textures.RedLeverUp
+ jne @f
+ cmp ecx, textures.RedLeverDown
+ jne @f
+ mov dword [level1.Grid + (10 * level1.Width + 12) * 4], level1.PassableArea
+ mov eax, .message_door2_opens
+ jmp .exit
+
+@@:
+ mov eax, .message_3levers
+ jmp .exit
+
+.message_3levers langstr0 ru, < \
+'Вы переставили этот рычаг в другое положение. ' >
+.message_door2_closed langstr0 ru, < \
+'Хоть вы и полны сил, но сдвинуть эту дверь хоть на ', 10, \
+'миллиметр у вас не получилось. ' >
+.message_door2_opens langstr0 ru, < \
+'До вас донёсся металлический скрежет. ' >
+endp
+
+proc level1.columns uses ebx ecx, action, cell_x, cell_y
+ mov eax, [action]
+ cmp ax, ACTION.USE_OBJECT
+ jne .exit_no_message
+
+ shr eax, 16
+ mov ebx, eax
+
+ mov eax, [cell_x]
+
+ cmp eax, 16
+ jne @f
+ cmp ebx, 1
+ jne .exit_no_message
+ stdcall remove_object_from_inventory, 1
+ mov eax, [level1.Column4TextureDescPtr]
+ mov [level1.Column1.TextureDescPtr], eax
+ mov [level1.Column1.DisableShading], 1
+ mov eax, .message_activated
+ jmp .exit
+
+@@: cmp eax, 19
+ jne @f
+ cmp ebx, 2
+ jne .exit_no_message
+ stdcall remove_object_from_inventory, 2
+ mov eax, [level1.Column4TextureDescPtr]
+ mov [level1.Column2.TextureDescPtr], eax
+ mov [level1.Column2.DisableShading], 1
+ mov eax, .message_activated
+ jmp .exit
+
+@@: cmp eax, 22
+ jne @f
+ cmp ebx, 3
+ jne .exit_no_message
+ stdcall remove_object_from_inventory, 3
+ mov eax, [level1.Column4TextureDescPtr]
+ mov [level1.Column3.TextureDescPtr], eax
+ mov [level1.Column3.DisableShading], 1
+ mov eax, .message_activated
+ jmp .exit
+
+@@:
+.exit_no_message:
+ xor eax, eax
+ ret
+
+.exit:
+ mov ecx, [level1.Column1.DisableShading]
+ and ecx, [level1.Column2.DisableShading]
+ and ecx, [level1.Column3.DisableShading]
+ jecxz @f
+ mov dword [level1.Grid + (11 * level1.Width + 21) * 4], level1.PassableArea
+@@:
+ ret
+
+.message_activated langstr0 ru, < \
+'Колонна начала светиться странным светом, а надпись ', 10, \
+'исчезла.' >
+endp
+
+proc level1.ogre action, cell_x, cell_y
+ mov eax, [action]
+
+ cmp ax, ACTION.DO_SOMETHING
+ jne @f
+ stdcall player_death
+ xor eax, eax
+ jmp .exit
+
+@@: cmp ax, ACTION.USE_OBJECT
+ jne .exit_default
+ shr eax, 16
+ cmp eax, 4
+ jne .exit_default
+
+ mov dword [level1.Grid + (15 * level1.Width + 13) * 4], level1.PassableArea
+ mov [level1.Ogre.Visible], 0
+ stdcall remove_object_from_inventory, 4
+ mov eax, .message_ogre
+ jmp .exit
+
+.exit_default:
+ xor eax, eax
+ dec eax
+.exit:
+ ret
+
+.message_ogre langstr0 ru, < \
+'- Ой! Монетка! А ну вернись!!! ' >
+endp
+
+proc level1.portal action, cell_x, cell_y
+ mov eax, [action]
+ cmp ax, AKODE_ACTION.CELL_ENTER
+ jne @f
+ stdcall game_over
+@@:
+ xor eax, eax
+ ret
+endp
\ No newline at end of file
diff --git a/programs/games/Dungeons/Resources/Levels/levels.inc b/programs/games/Dungeons/Resources/Levels/levels.inc
new file mode 100644
index 0000000000..b39b7150d1
--- /dev/null
+++ b/programs/games/Dungeons/Resources/Levels/levels.inc
@@ -0,0 +1,2 @@
+align 4
+include '1.inc'
\ No newline at end of file
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/CrazyWoodDoor.png b/programs/games/Dungeons/Resources/Textures/Environment/CrazyWoodDoor.png
new file mode 100644
index 0000000000..4f76a21de0
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/CrazyWoodDoor.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/GreenStone1.jpg b/programs/games/Dungeons/Resources/Textures/Environment/GreenStone1.jpg
new file mode 100644
index 0000000000..3e5616fec3
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/GreenStone1.jpg differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/GreenStone2.jpg b/programs/games/Dungeons/Resources/Textures/Environment/GreenStone2.jpg
new file mode 100644
index 0000000000..f486929036
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/GreenStone2.jpg differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/IronDoor.jpg b/programs/games/Dungeons/Resources/Textures/Environment/IronDoor.jpg
new file mode 100644
index 0000000000..30dc2c70f8
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/IronDoor.jpg differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/Portal.jpg b/programs/games/Dungeons/Resources/Textures/Environment/Portal.jpg
new file mode 100644
index 0000000000..420708da01
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/Portal.jpg differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/RedLeverDown.png b/programs/games/Dungeons/Resources/Textures/Environment/RedLeverDown.png
new file mode 100644
index 0000000000..e88cc7b010
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/RedLeverDown.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/RedLeverUp.png b/programs/games/Dungeons/Resources/Textures/Environment/RedLeverUp.png
new file mode 100644
index 0000000000..775b44c01c
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/RedLeverUp.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Environment/YellowStone1.jpg b/programs/games/Dungeons/Resources/Textures/Environment/YellowStone1.jpg
new file mode 100644
index 0000000000..4472c62c9b
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Environment/YellowStone1.jpg differ
diff --git a/programs/games/Dungeons/Resources/Textures/HUD/Death.png b/programs/games/Dungeons/Resources/Textures/HUD/Death.png
new file mode 100644
index 0000000000..265a12fd30
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/HUD/Death.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/HUD/End.png b/programs/games/Dungeons/Resources/Textures/HUD/End.png
new file mode 100644
index 0000000000..6b5e2bc842
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/HUD/End.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/HUD/LevelLoading.png b/programs/games/Dungeons/Resources/Textures/HUD/LevelLoading.png
new file mode 100644
index 0000000000..1ee2981b10
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/HUD/LevelLoading.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/HUD/Panel.png b/programs/games/Dungeons/Resources/Textures/HUD/Panel.png
new file mode 100644
index 0000000000..08b76a8fa9
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/HUD/Panel.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Licenses.txt b/programs/games/Dungeons/Resources/Textures/Licenses.txt
new file mode 100644
index 0000000000..36ce9bf6a5
--- /dev/null
+++ b/programs/games/Dungeons/Resources/Textures/Licenses.txt
@@ -0,0 +1,10 @@
+Textures/HUD/* - Anton_K, CC BY-SA 4.0
+
+Textures/Environment/*.png - CC0 1.0 (Public Domain)
+Textures/Environment/IronDoor.jpg - CC0 1.0 (Public Domain)
+Textures/Environment/*Stone*.jpg - p0ss, GNU GPL 2.0, http://opengameart.org/content/117-stone-wall-tilable-textures-in-8-themes
+Textures/Environment/Portal.jpg - Clint Bellanger, CC BY 3.0, http://opengameart.org/content/teleporter-circle
+
+Textures/Objects/*.png - CC0 1.0 (Public Domain)
+Textures/Objects/Chest.png - Daniel Cook, modified by Anton_K, CC BY 3.0, http://opengameart.org/content/planetcute-chest-closedpng
+Textures/Objects/Portal.png - Clint Bellanger, modified by Robert C., modified by Anton_K, CC BY 3.0, http://opengameart.org/content/teleporter-circle
\ No newline at end of file
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Chest.png b/programs/games/Dungeons/Resources/Textures/Objects/Chest.png
new file mode 100644
index 0000000000..2ba73c70cb
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Chest.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Coin.png b/programs/games/Dungeons/Resources/Textures/Objects/Coin.png
new file mode 100644
index 0000000000..c28090f6fe
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Coin.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Column1.png b/programs/games/Dungeons/Resources/Textures/Objects/Column1.png
new file mode 100644
index 0000000000..2b979a1214
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Column1.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Column2.png b/programs/games/Dungeons/Resources/Textures/Objects/Column2.png
new file mode 100644
index 0000000000..e753f02528
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Column2.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Column3.png b/programs/games/Dungeons/Resources/Textures/Objects/Column3.png
new file mode 100644
index 0000000000..a1bd83c7a9
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Column3.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Column4.png b/programs/games/Dungeons/Resources/Textures/Objects/Column4.png
new file mode 100644
index 0000000000..0bb3c25604
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Column4.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Ogre.png b/programs/games/Dungeons/Resources/Textures/Objects/Ogre.png
new file mode 100644
index 0000000000..69da9a7fc7
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Ogre.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Portal.png b/programs/games/Dungeons/Resources/Textures/Objects/Portal.png
new file mode 100644
index 0000000000..a423f4c3dd
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Portal.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Sun1.png b/programs/games/Dungeons/Resources/Textures/Objects/Sun1.png
new file mode 100644
index 0000000000..544e672cc0
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Sun1.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Sun2.png b/programs/games/Dungeons/Resources/Textures/Objects/Sun2.png
new file mode 100644
index 0000000000..e7c57a8fcd
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Sun2.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/Objects/Sun3.png b/programs/games/Dungeons/Resources/Textures/Objects/Sun3.png
new file mode 100644
index 0000000000..1425dd6c1d
Binary files /dev/null and b/programs/games/Dungeons/Resources/Textures/Objects/Sun3.png differ
diff --git a/programs/games/Dungeons/Resources/Textures/textures.inc b/programs/games/Dungeons/Resources/Textures/textures.inc
new file mode 100644
index 0000000000..d90a20ac5d
--- /dev/null
+++ b/programs/games/Dungeons/Resources/Textures/textures.inc
@@ -0,0 +1,69 @@
+;struct akode.TextureDesc
+; Type dd ? ; AKODE_TEXTURE_TYPE
+;
+; union
+; ImagePathPtr dd ?
+; Color dd ?
+; ends
+;
+; ImageDataPtr dd ?
+;
+; HasMagicPink db ? ; boolean
+; TileWalls db ? ; boolean
+; Usage db ? ; AKODE_TEXTURE_USAGE
+; VirtualScale db ? ; AKODE_VIRTUAL_SCALE_MODE, not supported yet
+; VirtualSize akode.Size ; (0, 0) for original size, not supported yet
+;ends
+
+align 4
+textures:
+
+.GreenStone1 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.GreenStone1, 0, 0, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+.GreenStone2 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.GreenStone2, 0, 0, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+
+.YellowStone1 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.YellowStone1, 0, 0, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+
+.CrazyWoodDoor akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.CrazyWoodDoor, 0, 1, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+.IronDoor akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.IronDoor, 0, 0, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+
+.RedLeverUp akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.RedLeverUp, 0, 1, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+.RedLeverDown akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.RedLeverDown, 0, 1, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+
+.PortalFloor akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.PortalFloor, 0, 0, 0, AKODE_TEXTURE_USAGE.ENVIRONMENT
+
+.Chest akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Chest, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+
+.Column1 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Column1, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+.Column2 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Column2, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+.Column3 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Column3, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+.Column4 akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Column4, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+
+.Ogre akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Ogre, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+
+.Portal akode.TextureDesc AKODE_TEXTURE_TYPE.IMAGE, texture_files.Portal, 0, 1, 0, AKODE_TEXTURE_USAGE.OBJECT
+
+texture_files:
+
+.GreenStone1 db 'Resources/Textures/Environment/GreenStone1.jpg', 0
+.GreenStone2 db 'Resources/Textures/Environment/GreenStone2.jpg', 0
+
+.YellowStone1 db 'Resources/Textures/Environment/YellowStone1.jpg', 0
+
+.CrazyWoodDoor db 'Resources/Textures/Environment/CrazyWoodDoor.png', 0
+.IronDoor db 'Resources/Textures/Environment/IronDoor.jpg', 0
+
+.RedLeverUp db 'Resources/Textures/Environment/RedLeverUp.png', 0
+.RedLeverDown db 'Resources/Textures/Environment/RedLeverDown.png', 0
+
+.PortalFloor db 'Resources/Textures/Environment/Portal.jpg', 0
+
+.Chest db 'Resources/Textures/Objects/Chest.png', 0
+
+.Column1 db 'Resources/Textures/Objects/Column1.png', 0
+.Column2 db 'Resources/Textures/Objects/Column2.png', 0
+.Column3 db 'Resources/Textures/Objects/Column3.png', 0
+.Column4 db 'Resources/Textures/Objects/Column4.png', 0
+
+.Ogre db 'Resources/Textures/Objects/Ogre.png', 0
+
+.Portal db 'Resources/Textures/Objects/Portal.png', 0
\ No newline at end of file
diff --git a/programs/games/Dungeons/data.inc b/programs/games/Dungeons/data.inc
new file mode 100644
index 0000000000..3d6ea054e6
--- /dev/null
+++ b/programs/games/Dungeons/data.inc
@@ -0,0 +1,91 @@
+include 'lang.inc'
+
+struc langstr [lng, data]
+{
+ if lang eq lng
+ sz ., data
+ end if
+}
+
+struc langstr0 [lng, data]
+{
+ if lang eq lng
+ sz0 ., data
+ end if
+}
+
+include 'AKODE/data.inc'
+include 'Resources/Textures/textures.inc'
+include 'Resources/Levels/levels.inc'
+
+FULLSCREEN = 0
+FSAA = 0
+DISABLE_SHADING = 0
+
+FIELD_OF_VIEW = 60
+BLOCK_BASE_SIZE = 512
+BLOCK_HEIGHT = 512
+
+INVENTORY_SIZE = 10
+
+HUD_PANEL_HEIGHT = 120
+
+INVENTORY_X = 570
+INVENTORY_Y = 15
+INVENTORY_PADDING_X = 10
+INVENTORY_PADDING_Y = 10
+OBJECT_IMAGE_WIDTH = 40
+OBJECT_IMAGE_HEIGHT = 40
+
+GAME_MESSAGE_X = 150 + 15
+GAME_MESSAGE_Y = 15 + 15
+GAME_MESSAGE_COLOR = 000000h
+
+if FULLSCREEN
+ ;MAIN_WINDOW_X = 0
+ ;MAIN_WINDOW_Y = 0
+ ;MAIN_WINDOW_WIDTH = 0FFFFh
+ ;MAIN_WINDOW_HEIGHT = 0FFFFh
+ MAIN_WINDOW_STYLE = (01000001b) shl 24
+ MAIN_WINDOW_STYLE2 = 1 shl 24
+else
+ MAIN_WINDOW_X = 150
+ MAIN_WINDOW_Y = 150
+ MAIN_WINDOW_WIDTH = 960
+ MAIN_WINDOW_HEIGHT = 720
+ MAIN_WINDOW_STYLE = (01110100b) shl 24
+ MAIN_WINDOW_STYLE2 = 0
+end if
+
+MAIN_EVENT_MASK = EVM_REDRAW or EVM_KEY or EVM_BUTTON or EVM_MOUSE or EVM_MOUSE_FILTER
+
+MAIN_WINDOW_TITLE langstr0 \
+ en, 'Dungeons of Augastes 0.1', \
+ ru, 'Подземелья Аугастеса 0.1'
+
+LevelLoadingImageFile db 'Resources/Textures/HUD/LevelLoading.png', 0
+HudPanelImageFile db 'Resources/Textures/HUD/Panel.png', 0
+DeathImageFile db 'Resources/Textures/HUD/Death.png', 0
+EndImageFile db 'Resources/Textures/HUD/End.png', 0
+
+align 4
+LevelLoadingImagePtr dd 0
+HudPanelImagePtr dd 0
+DeathImagePtr dd 0
+EndImagePtr dd 0
+
+ImageBufferPtr dd 0
+
+HudPanelNeedsRedraw dd 0
+
+GameMessage dd 0
+GameStatus dd 0
+
+GAME_STATUS.LEVEL_LOAD_FAILED = 1
+GAME_STATUS.DEAD = 2
+GAME_STATUS.END = 3
+
+ACTION.DO_SOMETHING = AKODE_ACTION.CUSTOM + 1
+ACTION.LOOK_AROUND = AKODE_ACTION.CUSTOM + 2
+ACTION.USE_OBJECT = AKODE_ACTION.CUSTOM + 3
+ACTION.LOOK_AT_OBJECT = AKODE_ACTION.CUSTOM + 4
\ No newline at end of file
diff --git a/programs/games/Dungeons/datadef.inc b/programs/games/Dungeons/datadef.inc
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/programs/games/Dungeons/import.inc b/programs/games/Dungeons/import.inc
new file mode 100644
index 0000000000..fbe622e067
--- /dev/null
+++ b/programs/games/Dungeons/import.inc
@@ -0,0 +1 @@
+include 'AKODE/import.inc'
\ No newline at end of file
diff --git a/programs/games/Dungeons/lang.inc b/programs/games/Dungeons/lang.inc
new file mode 100644
index 0000000000..30c0149bd9
--- /dev/null
+++ b/programs/games/Dungeons/lang.inc
@@ -0,0 +1 @@
+lang fix ru
\ No newline at end of file
diff --git a/programs/games/Dungeons/udata.inc b/programs/games/Dungeons/udata.inc
new file mode 100644
index 0000000000..8f4b27868b
--- /dev/null
+++ b/programs/games/Dungeons/udata.inc
@@ -0,0 +1,14 @@
+include 'AKODE/udata.inc'
+
+align 4
+StartupPath rb 1024
+
+ThreadInfoBuffer process_information
+
+MainWindowWidth dd ?
+MainWindowHeight dd ?
+
+WorldViewHeight dd ?
+
+PressedKeys rd 128 * 2
+Inventory rq INVENTORY_SIZE
\ No newline at end of file