01 апреля 2021

    Marsmare: Alienation
  На  конкурсе  Yandex Retro Games Battle 
первое  место  занял  платформер Marsmare: 
Alienation от команды Пьяная Муха (новички  
на Спектруме - с 2019 года!). Мы связались 
с  автором кода этой игры - Николаем Запо─ 
льновым. 

                 * * *

Alone> 
  Полистал код Marsmare: Alienation в де─ 
багере  и  обнаружил  там весьма необычный 
метод отрисовки. Какие примерно характери─ 
стики - сколько  он  поддерживает графики, 
сколько  одновременно  спрайтов на экране, 
есть  ли  клипирование  (если есть, то как 
оно работает)? 

Николай Запольнов> 
   Я изначально ориентировался на принцип,
описанный в этой статье: 
http://oldmachinery.blogspot.com/2014/04/ 
zx-sprites.html 
   Спрайты preshifted (и жрут кучу памяти,
надо  сказать, поэтому  их пришлось сильно
сжимать).
   Отрисовка идёт по таблице адресов (опи─
шу ниже принцип; отдельная таблица на каж─
дую   ширину   спрайта,  по   знакоместам: 
DrawRoutines8,DrawRoutines16 и 24 ),напри─ 
мер:

DrawRoutines8: 
       dw DirtyBits8 
4 раза: dw Skip8 7 раз, SkipEnd8 
3 раза: dw Draw8 7 раз, DrawEnd8 
        dw Draw8 7 раз, DrawBound8 
7 раз:  dw Draw8 7 раз, DrawEnd8 
        dw Draw8 7 раз, DrawBound8 
7 раз:  dw Draw8 7 раз, DrawEnd8 
        dw Draw8 7 раз, DrawBound8 
24 раза dw DrawSprite@@done 

Пропускает одну строку (не кратную 8): 
Skip8: 
       inc hl
       inc hl
       inc d                  ;Y0-Y2 += 1
       ret

Пропускает одну строку (кратную 8): 
SkipEnd8: 
       inc hl
       inc hl
       ex de,hl
       add hl,bc ;BC = 0xf920 ;Y0-Y2 -= 7
                               ;Y3-Y5 += 1
       ex de,hl
       ret

Рисует одну строку и переходит к следующей
строке (не кратной 8): 
Draw8: 
       ld a,(de)
       and (hl)
       inc hl
       or (hl)
       inc hl
       ld (de),a
       inc d                  ;Y0-Y2 += 1
       ret

Рисует одну строку и переходит к следующей
строке (кратной 8): 
DrawEnd8: 
       ld a,(de)
       and (hl)
       inc hl
       or (hl)
       inc hl
       ld (de),a
       ex de,hl
       add hl,bc ;BC = 0xf920 ;Y0-Y2 -= 7
                               ;Y3-Y5 += 1
       ex de,hl
       ret

Рисует одну строку и переходит к следующей
строке (на границе, где адресация экрана у
Спектрума перепрыгивает): 
DrawBound8: 
       ld a,(de)
       and (hl)
       inc hl
       or (hl)
       inc hl
       ld (de),a
       inc d ;before:Y0-Y2 = 7
              ;after: Y0-Y2 = 0,Y6-Y7 += 1
       ld a,e ;Y3-Y5 = 7
       add a,c ;0x20
       ld e,a ;Y3-Y5 = 0
       ret

   Рутина отрисовки спрайта:
  1) ставит SP на строку в таблице,соотве─
тствующую стартовой координате Y;
  2) заменяет   в  строке  в  таблице  для
последней    координаты    Y    адрес   на 
DrawSprite@@done  (сохраняя прошлое значе─ 
ние)
  3) делает RET.
   Каждая  рутина  отрисовки  делает RET и
прыгает  на  следующую, пока  не дойдет до 
DrawSprite@@done. В таблице они расставле─ 
ны  так, чтобы  правильно перепрыгивать по
адресам строк.
   По  возвращении в DrawSprite@@done вос─
станавливаем строку в таблице на начальное
значение.
   Клиппинг небольшой есть по вертикали за
счёт  Skip8, Skip8End  и DrawSprite@@done.
Строки  спрайта, которые попадают на верх─
ние  координаты,  не  рисуются.  А  строки
спрайта, которые  попадают на нижние коор─
динаты, принудительно  завершают рисование
спрайта. Но если выставить слишком большую
координату  Y, то промахнемся мимо таблицы
и будет креш.
   По горизонтали клиппинга нет.
   Для разных размеров спрайтов дополните─
льно есть небольшая табличка-дескриптор:

SprOff16x16_8: 
       dw 0
      repeat 7,Y
       dw (16*4 + 2) + (16*6 + 2) * Y
      endrepeat
       db 16*2 ;height in pixels * 2
       db 2,1,2 ;num of dirty vert tiles
             ;(default, align16, lessEq8)
       dw DrawSprite@@exitSP

   Последнее слово - адрес процедуры заве─
ршения  отрисовки. Там  ещё  может  лежать 
DrawSprite@@coloredXX для цветных спрайтов 
(пилюли,   патроны,  кристаллы,  топливо),
он    дополнительно    атрибуты     пишет. 
DrawSprite@@exitSP - обычный  выход, такой 
у большиства спрайтов.
  Num of dirty  vert tiles  -  количество
тайлов  (тайлы - 16x16 ), которые  пачкает
спрайт (они будут перерисованы в следующем
кадре, чтобы закрасить спрайт перед рисов─
кой нового кадра). Три числа вместо одного
для оптимизации: для общего случая (не оп─
тимизированный), для случая,когда Y кратен 
16, и когда (Y & 15) <= 8. 
   Первые  8  слов  -  смещения  от начала
спрайта  для каждого смещения по X. Спрайт
выглядит  так  (смещения как раз указывают
на соответствующий dw DrawRoutinesXX ):

dw SprOff16x16_8 
dw DrawRoutines16 
db 0xe1,0x00,0x87,0x00 ; ...____..____... 
db 0xc1,0x0C,0x07,0x30 ; ..__##_.__##_... 
db 0xc0,0x10,0x07,0x40 ; .._#_____#___... 
db 0xc0,0x0F,0x1f,0x80 ; ..__#####__..... 
db 0x80,0x10,0x0f,0x40 ; .__#_____#__.... 
db 0x80,0x20,0x07,0xA0 ; ._#_____#_#__... 
db 0x80,0x2C,0x07,0x50 ; ._#_##___#_#_... 
db 0x80,0x26,0x03,0x10 ; ._#__##____#__.. 
db 0x80,0x06,0xc3,0x08 ; .____##_..__#_.. 
db 0xc0,0x16,0xe1,0x08 ; .._#_##_..._#__. 
db 0xc0,0x0C,0xe1,0x04 ; ..__##__...__#_. 
db 0xc1,0x10,0xf1,0x04 ; .._#___....._#_. 
db 0xc0,0x08,0xf1,0x04 ; ..__#___...._#_. 
db 0xe0,0x06,0x01,0x04 ; ...__##______#_. 
db 0xf0,0x01,0x01,0xF8 ; ....___######__. 
db 0xfc,0x00,0x03,0x00 ; ......________.. 
dw DrawRoutines24 
db 0xf0,0x00,0xc3,0x00,0xff,0x00 
db 0xe0,0x06,0x83,0x18,0xff,0x00 
db 0xe0,0x08,0x03,0x20,0xff,0x00 
db 0xe0,0x07,0x0f,0xC0,0xff,0x00 
db 0xc0,0x08,0x07,0x20,0xff,0x00 
db 0xc0,0x10,0x03,0x50,0xff,0x00 
db 0xc0,0x16,0x03,0x28,0xff,0x00 
db 0xc0,0x13,0x01,0x08,0xff,0x00 
db 0xc0,0x03,0x61,0x04,0xff,0x00 
db 0xe0,0x0B,0x70,0x04,0xff,0x00 
db 0xe0,0x06,0x70,0x02,0xff,0x00 
db 0xe0,0x08,0xf8,0x02,0xff,0x00 
db 0xe0,0x04,0x78,0x02,0xff,0x00 
db 0xf0,0x03,0x00,0x02,0xff,0x00 
db 0xf8,0x00,0x00,0xFC,0xff,0x00 
db 0xfe,0x00,0x01,0x00,0xff,0x00 
...ещё 6 блоков с DrawRoutines24... 
       (все сдвиги по X)

   В спрайте - AND-маска и OR-маска.
   Количество  одновременных  спрайтов  на
экране  сильно зависит от размера спрайтов
и количества испачканных квадратов (напри─
мер,спрайт 8x8, стоящий между двумя тайла─
ми, будет есть больше ресурсов, чем выров─
ненный на границу знакоместа, так как надо
стирать больше тайлов).
   В целом, получается 1 спрайт игрока + 3
больших  врага  ( 16x24, при  некратных  8
координатах X - 24x24 ) и несколько спрай─
тов-выстрелов ( 8x8 ). На картах, где  ещё
были анимированные тайлы или другие спрай─
ты  (лифт, шлюз и т. п.), ставили поменьше
врагов.

   Тайлы рисуются отдельными рутинами.Весь
экран - карта  тайлов  16x10  (для текущей
карты  хранятся слова - прямые адреса тай─
лов  в памяти, чтобы при отрисовке не рас─
считывать  адреса; сами данные карт хранят
один  байт для экономии места). Дополните─
льно есть битовая маска проходимости и две
маски  грязных тайлов (по одной на обычный
и теневой экранный буфер).
   Данные тайлов занимают 36 байт ( 32 ба─
йта  пиксели и 4 байта атрибуты)  и идут в
чередующемся порядке:
                1 -> 2 |
                4 <- 3 v
   Такой порядок позволяет сэкономить нем─
ножко тактов на обновлении адресов в реги─
страх.

Alone> 
  Как  я понимаю, спрайты должны лежать в 
нижней памяти,т.к.вывод попеременно ведёт─ 
ся в нижний и верхний экран? 

Николай Запольнов> 
   Да, в нижней памяти (банки 2 и 5 ),либо
в банке 7 (где и находится теневой экран).
Очень не хватает, конечно, возможности ма─
пить память на ROM.

Alone> 
  Пилюли,патроны и т.п. сделаны не тайла─ 
ми,чтобы могли накладываться на любой фон? 

Николай Запольнов> 
   Чтобы  они могли накладываться на фон и
чтобы  было  легко реализовать возможность
их подбирать.Кроме того,изначально мы сде─
лали  их  обычными спрайтами, а раскрасить
решили потом (они были плохо заметны), по─
этому  добавить возможность рисовать атри─
буты  вместе  со спрайтом выглядело лучшим
решением,чем переписывать всё на использо─
вание тайлов.

Alone> 
  А как сделана  анимация тайлов? Для те─ 
кущей локации создаётся  список изменяемых 
тайлов,а потом всё вручную? Как это описы─ 
вается в редакторе? 

Николай Запольнов> 
   В редакторе  это  никак не описывается,
анимации  заданы  для  конкретных тайлов в
lua-скрипте  (как  и задержки для анимаций
тайлов).
   Для  каждой  карты  формируется  список
анимированных тайлов:

db 3 ;numAnimatables 
;animatable 1 
db 1,0 ;delay,counter 
db 2,0 ;count,index 
dw MapAnim_358e04958311f5a6b5f9 ;tile list 
dw Map_05_08@@anim1 - Map_05_08 ;offset 
                            ;into map data 
db 12,4,0 ;mapY,mask1,mask2 
dw 420 ;target screen address 
;animatable 2 
db 4,0 ;delay,counter 
db 8,0 ;count,index 
dw MapAnim_6aee9a687735e6fe7e67 ;tile list 
dw Map_05_08@@anim2 - Map_05_08 ;offset 
                           ;into map data 
db 12,16,0 ;mapY,mask1,mask2 
dw 424 ;target screen address 
;animatable 3 
db 4,0 ;delay,counter 
db 8,0 ;count,index 
dw MapAnim_6aee9a687735e6fe7e67 ;tile list 
dw Map_05_08@@anim3 - Map_05_08 ;offset 
                            ;into map data 
db 12,32,0 ;mapY,mask1,mask2 
dw 426 ;target screen address 
    ... 
MapAnim_6aee9a687735e6fe7e67: 
dw MapTile176 * 36 + TILES_BASE 
dw MapTile177 * 36 + TILES_BASE 
dw MapTile178 * 36 + TILES_BASE 
dw MapTile179 * 36 + TILES_BASE 
    ... 

   И вот такой рутиной обновляется: 
; Input: None 
; Output: None 
; Preserves: A', BC', DE', HL', IXH 
; Trashes: A, BC, DE, HL, IXL, IY 
UpdateMapAnimatedTiles: 
       ld a,(NumMapAnimatables)
       or a
       ret z
       pushAllowWrite MapAnimatables,
                     MAX_ANIMATABLES * 13
       pushAllowWrite MapTiles, 16*10*2
       pushAllowWrite MapDirty1,
                  MapDirtyEnd - MapDirty1
       ld hl,MapAnimatables
       ld ixl,a 
@@loop: ld c,(hl) ;delay 
       inc hl
       ld a,(hl) ;timer
       inc a     ;increase timer
       cp c      ;reached delay?
       jr z,@@1
       ld (hl),a ;store new timer value
       inc hl
       inc hl
       ld a,(hl) ;index
       jp @@3 
@@1:    xor a 
       ld (hl),a ;reset timer
       inc hl
       ld c,(hl) ;count
       inc hl
       ld a,(hl) ;index
       inc a     ;next frame
       inc a
       cp c      ;reached limit?
       jr nz,@@2
       xor a 
@@2:    ld (hl),a ;store new index 
@@3:    inc hl 
       ld e,(hl)
       inc hl
       ld d,(hl) ;DE = list of tiles
       inc hl
       ex de,hl ;save HL
       ld b,0    ;HL = list of tiles
       ld c,a
       add hl,bc ;add index to HL
       ld a,(hl) ;HL = new tile address
       inc hl
       ld h,(hl)
       ld l,a
       ex de,hl ;restore HL
       ld iy,32  ;DE = new tile address
       add iy,de ;IY = attribute address 
;load target address into BC 
       ld a,(hl) ;BC=offset into map data
       inc hl
       add a,0xff&MapData
       ld c,a
       ld a,(hl)
       adc a,0xff&(MapData>>8)
       ld b,a
       inc hl 
;write new tile to target address 
       ld a,e
       ld (bc),a
       inc bc
       ld a,d
       ld (bc),a 
;mark tile as dirty 
       ld b,0
       ld c,(hl) ;BC=offset into dirtymap
       inc hl 
;update dirty map 
       ex de,hl  ;save HL into DE
       ld hl,(MapDirtyBack)
       add hl,bc ;HL = corresponding line in MapDirty
       ld a,(de) ;first mask bit
       or (hl)   ;apply
       ld (hl),a ;store into dirty map
       inc hl
       inc de
       ld a,(de) ;second mask bit
       or (hl)   ;apply
       ld (hl),a ;store into dirty map
       inc de
       ex de,hl  ;restore HL 
;update attributes 
       ld e,(hl)
       inc hl
       ld d,(hl) ;DE = target screen addr
       inc hl
       ld b,iyh  ;BC = tile attributes
       ld c,iyl
       call DrawMapTileAttributes@@1 
;next iteration 
       dec ixl
       jr nz,@@loop
       popAllowWrite MapDirty1,
                  MapDirtyEnd - MapDirty1
       popAllowWrite MapTiles, 16*10*2
       popAllowWrite MapAnimatables,
                     MAX_ANIMATABLES * 13
       ret

   Суть процедуры состоит в том, чтобы:
  а) прописать  новый адрес тайла в распа─
кованные данные текущей карты в памяти;
  б) поставить грязный флаг для тайла,что─
бы при следующем обновлении экрана его пе─
рерисовала та же процедура,которая стирает
спрайты;
  в) заменить атрибуты на экране (процеду─
ра  стирания  тайлов  атрибуты не трогает,
чтобы сэкономить время,так как большинство
спрайтов красятся в цвет фона; коду, кото─
рому нужно перекрасить атрибуты,приходится
вызывать DrawMapTileAttributes отдельно).

Alone> 
  У  тебя свой редактор карт? Как там де─ 
лается  привязка  событий  к точкам карты? 
Есть какая-то система скриптования? 

Николай Запольнов> 
   У  меня  всё  своё: ассемблер, редактор
спрайтов,редактор карт,полная IDE.Включает
в себя еще допиленный напильником эмулятор
Fuse  (и интегрирует с ним отладчик), ком─ 
пилятор  SDCC, bas2tap, tap2wav  и  другие
утилиты.Есть редактор графики и тайлсетов.
Инструменты  для  импорта и экспорта PNG и 
SCR. На самом деле там куча багов,граблей, 
тормозов и недоделок. Я потихонечку, когда
есть  минутка, работаю над улучшенной вер─
сией.
   От  ассемблера мне нужно было много ве─
щей,которых не хватало в имеющихся инстру─
ментах:
  1) генерация  отладочной  информации для
отладчика (строка в файле, адреса перемен─
ных - хотя  их я так и не доделал в отлад─
чике...).
  2) быстрый  и удобный расчет T-state для
инструкций  (у меня они отображаются в IDE
справа от номера строки).
  3) расширенные макро-инструменты (напри─
мер,у меня есть мета-инструкции для прове─
рки выхода за границы памяти, и мой эмуля─
тор проверяет операции чтения/записи; что-
то вроде valgrind для ПК - кучу багов пой─
мал с помощью них).

Alone> 
  Это  макросы  pushAllowWrite MapDirty1, 
MapDirtyEnd - MapDirty1? Там адрес и длина 
разрешённых адресов записи? 

Николай Запольнов> 
   Да,  первый  аргумент - адрес, второй -
длина.
   Макросы  push кладут на стек разрешение
записи в указанную область, pop убирают их
со  стека (в pop нужно передавать такие же
значения, как и в push - эмулятор проверя─
ет, что порядок push/pop не нарушен).
   Макросы  прописываются в дебаг-информа─
цию  и привязываются к адресу следующей за
ними  инструкции. Когда эмулятор выполняет
инструкцию, он  проверяет, есть ли там эти
макросы,и так же их выполняет. А при обра─
ботке  операций  чтения/записи  в память -
проверяет текущий стек на предмет,разреше─
на ли запись. Если не разрешена,останавли─
вает  выполнение  и кидает в отладчик, так
же,как и брейкпоинт. Можно посмотреть код,
регистры  и т. п. и при желании продолжить
выполнение.
  4)  расширенный  инструментарий работы с
секциями. Я могу указывать адреса, в какие
файлы  писать секции, сжатие посекционно и
т. п. Плюс  на  выходе генерируется полная
карта памяти, что мне очень помогло.
   ... 
section bank2_langmenu 
[file "BANK2"] 
section z_intro_strings_ru 
[file "BANK2", compress=lzsa2] 
section z_intro_strings_en 
[file "BANK2", compress=lzsa2] 

section bank3 
[file "BANK3", base 0xc000] 

section bank4 
[file "BANK4", base 0xc000] 
section bank4_data_alien1 
[file "BANK4", compress=lzsa2] 
   ...

   Карта памяти выглядит так:

Data_tiles_22_A
 0xDE16/56854..0xDE33/56833 36/30 byte(s) 
...ещё 18 строк в таком же стиле... 

BANK1:imaginary> 
Bank1_music_buffer_bss
 0xE084/57476..0xFDE6/64998 7523 byte(s)
Bank1_engine_bss
 0xFDE7/64999..0xFFFF/65535 537 byte(s)

BANK2> 
   ...

Alone> 
  36/30 byte(s) - это  размер выделенного 
места и фактически занятое место? 

Николай Запольнов> 
   Да, первое число - оригинальный размер,
второе - после сжатия.
   На самом деле 36-байтные куски - плохой
пример  для сжатия :) Это единичные тайлы,
которые я вкрячивал куда попало уже в пос─
ледний момент, чтобы  разместить  побольше
тайлов. Они  раскиданы  по разным местам в
памяти, и некоторые  плохо  жмутся  (можно
найти, например, 36/38, т.е. сжатая версия
больше, чем  оригинал); но  добавлять  для
каждого  тайла  флаг, сжат он  или  нет, и
делать две ветки кода  получается длиннее,
чем потерять несколько байт на несжавшихся
тайлах.

   Для  bas2tap я добавил пару псевдоинст─
рукций, чтобы, например встраивать ассемб─
лерный код сразу в инструкцию REM. Вот так
выглядит исходник boot.bas:

0 REM @{loader} 
10 LET A=VAL"23635": RANDOMIZE USR(PEEK(A+ 
PI/PI)*VAL"256"+PEEK A+VAL"5") 

   В сборщик  интегрирован  движок  Lua, и
вся  сборка написана на Lua. На самом деле
там ужаснейшие скрипты  по несколько тысяч
строк,в которых уже сам черт ногу сломит:)
   Привязка  событий  к точкам карты дела─
ется через строковые значения,которые пар─
сятся lua-скриптом. Каждому тайлу на карте
можно сопоставить  строку (или несколько),
нажав  правой  кнопкой  мыши. В  редакторе
карты они  обозначены жёлтым прямоугольни─
ком с надписью поверх игрового экрана.

Alone> 
  А что получается  в результате выполне─ 
ния скрипта сборки на Lua? 

Николай Запольнов> 
   Скрипты генерируют даже не бинарники, а
исходники на ассемблере.Помогает в отладке
скриптов.
   Выше  показан  пример кода спрайта - он
сгенерён lua-скриптом;список анимированных
тайлов - тоже. Вот такой массив, например,
генерируется с информацией о врагах:

section bank0_data_enemies 
MapEnemyInfo: 
@@1: 
db 3 | (Offset_SpaceFlyingAlienAI << 2) 
                ;EnemyInfo_spriteCntAndAI 
db 64,104 ;EnemyInfo_origX, _origY 
db 0,0,24 ;EnemyInfo_x, _y, _h 
db 4 | (4<<3) | 0, 0, 0 ;_healthAndFlags, 
                   ;_stateAndFlags, _time 
@@2: 
db 3 | (Offset_SpaceFlyingAlienAI << 2) 
                ;EnemyInfo_spriteCntAndAI 
db 176,104 ;EnemyInfo_origX, _origY 
db 0,0,24 ;EnemyInfo_x, _y, _h 
db 4 | (4<<3) | 0, 0, 0 ;_healthAndFlags, 
                    ;_stateAndFlags, _time
   ...

   В общем, всякие данные из спрайтов,карт
и прочего в удобном для движка виде.
   Ещё я делал себе вспомогательный вывод,
например, дампил информацию,в каких картах
присутствуют те или иные тайлы:

   ... 
gfx/tiles_NEW/12_06_lab_wall_center.gfx 
   09_05
   09_06
   10_07 
gfx/tiles_NEW/00_06_starport_mid2.gfx 
   04_03
   04_04
   04_05 
gfx/tiles_NEW/01_00_human_bottle.gfx 
   10_05
   10_07
   Zintr1 
gfx/tiles_NEW/01_00_human_bottle.gfx 
   10_05
   10_07
   Zintr1
   ...

   Или   вот,  например,  генерировал   из
lua-скрипта целую карту в PNG, чтобы можно
было  оценить, как они стыкуются между со─
бой, и искать ошибки.

Alone> 
  Как  описываются  анимации  спрайтов? В 
чём  они  редактируются? Я хотел применить 
Pixelorama, но  она не поддерживает палит─  
ровый режим и одновременное редактирование 
нескольких анимаций с использованием общих 
слоёв. И я не понял, как  оттуда выгрузить 
задержки  с точностью 1/50 с (GIF такие не 
поддерживает). 

Николай Запольнов> 
   Редактор анимаций, хех :)) Хотелось бы.
Пока  что кадры и задержки анимаций описы─
ваются в коде...

_AlienWalkSprites: 
       dw _alienGunWalkRight1
       dw _alienGunWalkRight2
       dw _alienGunWalkRight3
       dw _alienGunWalkRight4
       dw _alienGunWalkLeft1
       dw _alienGunWalkLeft2
       dw _alienGunWalkLeft3
       dw _alienGunWalkLeft4
       ... 
@@doWalk: 
       ld a,(ix+EnemyInfo_time)
       rrca
       rrca
       and 0x3f
       ld e,a
       ...

   Для анимированных тайлов - захардкожены
в maps.lua:

If animName==gfx/tiles/acid/acid_anim.gfx 
or animName==gfx/tiles/acid/ 
                   acid_top_anim.gfx then
   delay = 5
   deadlyArea[y * mapW + x] = true 
... 
elseif animName==gfx/level_elements/ 
            terminal_bottom_anim.gfx then
   delay = 10 
... 

  delay  попадает  в блок данных карты об
анимированных тайлах (выше был пример спи─
ска анимированных тайлов).

Alone> 
  Что значит DeadlyArea[y*MapW+x] = true?

Николай Запольнов> 
   DeadlyArea - это  массив "смертельных"
тайлов. Туда пишется true для шипов и кис─
лоты и false для обычных тайлов. Они потом
сохраняются  скриптом  в отдельный блок, и
движок  проверяет  пересечения персонажа с
этими тайлами. Если игрок попадает на тайл 
DeadlyArea, он получает ранение. 

Alone> 
  Будут ли  выложены  исходники  игры? По 
поводу  их  вида - ничего страшного :) Ты, 
наверно, уже видел,как выглядят исходники, 
которые спектрумисты обычно выкладывают :) 

Николай Запольнов> 
   Видел.Но тут еще прибавляется собствен─
ный ассемблер, скрипты на Lua,генерирующие
спрайты, и прочие удовольствия.
   Шрифты  генерируются одним скриптом и в
одном  формате, тайлы - другим  и в другом
(плюс  там адская упаковка и перетасовка в
памяти, делалось  под  конец  и впопыхах),
спрайты - третьим скриптом, тоже есть нес─
колько вариантов - ч/б и цветные.
   Спрайты   распаковываются  и  подогнаны
так, чтобы занимать одинаковые адреса (на─
пример, код всегда адресует спрайты игрока
по одним адресам, а на их места распаковы─
ваются  спрайты  с пушкой или в скафандре,
когда игрок подбирает соответствующие пре─
дметы; такая же история со спрайтами обыч─
ного  элиена и элиена с пистолетом, напри─
мер).
   К тому же, в памяти  сейчас  всё  очень
плотно, я в последние  дни  ещё  навёл там
хаоса...



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
Events - a detailed report and photos from Chaos Construction 2001 (ending).
New Programs - copier ABCDcopy and utility for formatting floppy disks Floppy Format.
Fantasy - Winnie the Pooh - 10 years later.
conversations - New section, which discusses the concerns of users. City Mednonogovsk in the game, "UFO. The enemy is unknown." Additional lives in the game "Prince of Persia".

В этот день...   21 November