Captain Drexx изнутри. Часть 4. Towers
Итак, крипы лезут. Нужно обороняться.
Содержание цикла «Captain Drexx изнутри»
Каждая установленая башня должна просматривать свою область видимости и наносить урон в зависимости от своего типа и уровня.
Что имеем: список башен, скорость, сила стрельбы, несколько апгрейдов, которые влияют на мощность поражения / время действия freeze, и начальную стоимость.
данные о установленных на уровне башнях хранятся списком, как и множество данных в игре.
Кратко: тип башни, её место на карте, скорость стрельбы, счётчик задержки перед следующим выстрелом, текущий спрайт выстрела, область поражения относительно места расположения.
Остановимся на этом подробнее: область пражения описывается как клетки на карте относительно самой башни. Учитываем, что башня не может стоять на пути крипов, карта имеет вид 16х11 клеток. Описываем как отступы:
0 — это позиция базы, 1 — клетка справа, -16 — клетка сверху.
Итак, для каждого крипа перебираем все башни и выполняем необходимое в данный момент времени.
Проверяем, есть ли крипы на тропинке, берём их количество.
Берём первого, вычисляем стоит ли ним заниматься. Если нужно — перебираем башни и смотрим, попадает-ли он в их область поражения.
Башни закончились, берём следующего крипа
Перебираем список зоны поражения башни с проверкой, там ли крип:
по признаку конца списка башен переходим на следующего крипа
Крип к зоне поражения башни. Возможно, нужно выстрелить? НО!
Башни должны работать в нормальном режиме, не перегреваясь :) задержка после выстрела
Да, стреляем!
Если стреляет башня фриза, то примораживаем крипа.
Если башня любого другого типа — поджариваем крипа и уменьшаем его энергию на значение мощности башни.
УБИЛИ 8)
добавим себе очки, отметим что-бы они обновились.
Переходим к следующему крипу
Обновляем инфу на экране после обработки всех башен:
После отработки логики башен стоит обновить их состояние на экране
Перебираем башни и отрисовываем их спрайты.
Выстрел!
Вычислен адрес положения башни по типу в памяти
Отрисовали, пропускаем зону поражения из списка башен
Думаю, тут довольно просто было.
А вот как происходит установка башни:
в bc мы имеем данные о типе башни и её положении на экране (в клетках игрового поля)
Смотрим характеристики на предмет мощности огня башни
Пробиваем по карте, какие именно клетки подвержены огню. неюзаемые отбрасываем
Выбрана зона поражения для данного типа башни.
проверяем на верхнуюю/нижнюю границу карты. плохо, когда можно поставить башню в самом верху, а бить врагов в самом низу :)
проверка, есть ли путь крипов в этом месте
подходит, сохраняем клетку в списке зоны поражения башни
отмечаем конец зоны поражения
замыкаем список башен, начинаем отрисовывать башенку
отрисовка в 2 экрана сразу
Когда игра уже была закончена, я понял, что алгоритм стрельбы башен мог быть другим.
Сейчас по каждому крипу перебираются все башни. Можно было-бы смотреть на каждую башню, перебирая всех крипов.
Но, хорошенько подумав, я понял, что такое изменение:
Содержание цикла «Captain Drexx изнутри»
Каждая установленая башня должна просматривать свою область видимости и наносить урон в зависимости от своего типа и уровня.
Что имеем: список башен, скорость, сила стрельбы, несколько апгрейдов, которые влияют на мощность поражения / время действия freeze, и начальную стоимость.
tower_list ds 5
towers_fire_rate ; fire rate
db #1a,#14,#0e,#20
; power of fire
tower_char db 8
db 5
db 4
db 60
; power of upgrade
;price,type, level2, level3, end
tower_upgrade db 5, 0, 8, 11,20,#ff
db 8, 1, 5, 9, 14,#ff
db 12,2, 4, 6, 9, #ff
db 5, 3, 60, 120, 190,#ff
tower_price db #08, #0c, #14, #0a
данные о установленных на уровне башнях хранятся списком, как и множество данных в игре.
; towers
; 0 type =>tower_char db
; 1 position
; 2 fire rating
; 3 fire rating counter
; 4 num_spr
; 5 -... ranged map cells
; #ff - end of tower
; #fe - end of all towers
Кратко: тип башни, её место на карте, скорость стрельбы, счётчик задержки перед следующим выстрелом, текущий спрайт выстрела, область поражения относительно места расположения.
Остановимся на этом подробнее: область пражения описывается как клетки на карте относительно самой башни. Учитываем, что башня не может стоять на пути крипов, карта имеет вид 16х11 клеток. Описываем как отступы:
tower_range1 db -17,-16,-15,-1,1,15,16,17,#80
0 — это позиция базы, 1 — клетка справа, -16 — клетка сверху.
Итак, для каждого крипа перебираем все башни и выполняем необходимое в данный момент времени.
towers_fire ld ix,enemy
ld a,(enemy_count)
or a
ret z
ld b,a
Проверяем, есть ли крипы на тропинке, берём их количество.
tf push bc
ld a,(ix+0) ; #ff - killed
inc a
jp z,t_e_next
ld l,(ix+5)
ld h,(ix+6)
dec hl
ld a,h
or l
jp nz,t_e_next
Берём первого, вычисляем стоит ли ним заниматься. Если нужно — перебираем башни и смотрим, попадает-ли он в их область поражения.
ld hl,towers-1
tf_next inc hl
ld a,(hl) ; tower type
cp #fe
jr z,t_e_next
Башни закончились, берём следующего крипа
ld (freez+1),a
inc hl
inc hl
ld (fire_timeout+1),hl
ld (fire_timeout_init+1),hl
inc hl
ld a,(hl)
ld (fire_to_init+1),a
inc hl
ld (tower_fires_add+1),hl ; num_spr
inc hl
ld c,(hl) ; power of tower
inc hl
Перебираем список зоны поражения башни с проверкой, там ли крип:
tf0 ld a,(hl) ; range towers
cp #ff ; end of current tower ranged list
jr z,tf_next
по признаку конца списка башен переходим на следующего крипа
; position on way, num_spr, napravlenie, scr_adr, wait low,high, type, energy
; 0 1 2 3,4 5,6 7 8
cp (ix+0)
jr nz,tf1
push hl
fire_timeout ld hl,0
dec (hl)
pop hl
Крип к зоне поражения башни. Возможно, нужно выстрелить? НО!
Башни должны работать в нормальном режиме, не перегреваясь :) задержка после выстрела
jr nz,tf1
Да, стреляем!
push hl
fire_timeout_init
ld hl,0
fire_to_init ld a,0
ld (hl),a
pop hl
ld a,7
tower_fires_add ld (0),a
freez ld a,0
cp 3
jr nz,not_freezing
ld a,c ; freeze_power
ld (ix+11),a
jr tf1
Если стреляет башня фриза, то примораживаем крипа.
Если башня любого другого типа — поджариваем крипа и уменьшаем его энергию на значение мощности башни.
not_freezing ld (ix+10),8 ; enemy fireed
push hl
ld l,(ix+8)
ld h,(ix+9) ; ranged enemy, energy -= power of tower
ld b,0
sbc hl,bc
ld (ix+8),l
ld (ix+9),h
jr nc,tf11
ld (ix+0),#ff ; enemy killed
ld (ix+1),7 ; sprites of the DEADd ;)
УБИЛИ 8)
добавим себе очки, отметим что-бы они обновились.
ld hl,score
inc (hl)
ld a,(money)
inc a
ld (money),a
call dec_enemy_counter
xor a
ld (view_scores_update_flag+1),a
tf11 pop hl
tf1
inc hl
jr tf0
Переходим к следующему крипу
t_e_next ld de,12
add ix,de
pop bc
dec b
jp nz,tf
Обновляем инфу на экране после обработки всех башен:
view_scores_update_flag
ld a,0
or a
ret nz
view_scores_update
call view_score
ld a,(money)
ld de,money_adr
call view_dec_energy
call dec_enemy_view
ld a,(lives)
ld (view_scores_update_flag+1),a
ld de,lives_adr
jp view_dec_energy
После отработки логики башен стоит обновить их состояние на экране
Перебираем башни и отрисовываем их спрайты.
tower_fires_view
ld hl,towers
tfv0 ld a,(hl)
cp #fe
ret z
ld b,a ; type
inc hl
ld a,(hl) ; pos
inc hl
push hl
call tower_screen_adr
ex de,hl
pop hl
inc hl ; rate
inc hl ;rate_count
ld a,(hl) ; num_spr
or a
jr z,tfv2
dec (hl)
dec a ; стрельба, если начинаем проигрывание спрайта фаера башни
cp 6
jr nz,tfv2
push hl
push de
push bc
push af
ld a,r
and 3
call AFXPLAY
Выстрел!
pop af
pop bc
pop de
pop hl
inc a
tfv2 srl a
push hl
push de ; de - screen
ld l,a
ld h,0
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ex de,hl
ld l,b
ld h,0
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl ; x 128
add hl,de
ld de,tower_gfx
add hl,de
Вычислен адрес положения башни по типу в памяти
pop de
di
ld (tfvs+1),sp
ld sp,hl
ex de,hl
dup 7
pop de
ld (hl),e
inc l
ld (hl),d
dec l
inc h
edup
pop de
ld (hl),e
inc l
ld (hl),d
dec l
INC H
LD A,H
AND 7
jr NZ,tfvsn
LD A,L
ADD A,32
LD L,A
jr C,tfvsn
LD A,H
SUB 8
LD H,A
tfvsn
dup 7
pop de
ld (hl),e
inc l
ld (hl),d
dec l
inc h
edup
pop de
ld (hl),e
inc l
ld (hl),d
tfvs ld sp,0
ei
pop hl
Отрисовали, пропускаем зону поражения из списка башен
tfv3 inc hl
ld a,(hl)
cp #ff
jr nz,tfv3
inc hl
jp tfv0
Думаю, тут довольно просто было.
А вот как происходит установка башни:
в bc мы имеем данные о типе башни и её положении на экране (в клетках игрового поля)
tower_create ld hl,towers_count
inc (hl)
; a: fire rate
; create tower, b: pos #21, c: type 0
tc_adr ld hl,towers
ld (hl),c ; type
inc hl
ld (hl),b ; position
inc hl
ld (hl),1 ; init fire rating
inc hl
ld (hl),a ; fire rating counter
inc hl
ld (hl),6 ; num_spr
inc hl
ld e,c
push hl
ld d,0
ld hl,tower_char
add hl,de
Смотрим характеристики на предмет мощности огня башни
ld a,(hl) ; power of tower
pop hl
ld (hl),a
inc hl
ex de,hl
Пробиваем по карте, какие именно клетки подвержены огню. неюзаемые отбрасываем
ld hl,(map)
ld a,c
; range check
or a
jr nz,tr2
ld ix,tower_range1
jr tr
tr2 cp 1
jr nz,tr3
ld ix,tower_range2
jr tr
tr3 cp 2
jr nz,tr4
ld ix,tower_range3
jr tr
tr4 ld ix,tower_range1
Выбрана зона поражения для данного типа башни.
; create range map
tr ld a,l
add b ; pos of tower
ld l,a
ld (tr_base+1),hl
tr0 ld a,(ix+0)
cp #80
jr z,trn0
tr_base ld hl,0 ; base_point
add l
up_map_lim cp 0
jr c,trn1
dwn_map_lim cp 176
jr nc,trn1
проверяем на верхнуюю/нижнюю границу карты. плохо, когда можно поставить башню в самом верху, а бить врагов в самом низу :)
ld l,a
ld a,(hl)
inc a
jr z,trn1
проверка, есть ли путь крипов в этом месте
dec a
ld (de),a
inc de
подходит, сохраняем клетку в списке зоны поражения башни
trn1 inc ix
jr tr0
trn0 ld a,#ff
ld (de),a ; #ff - end of ranged map cells
отмечаем конец зоны поражения
inc de
ex de,hl
ld (tc_adr+1),hl
ld (hl),#fe ; #fe - end of all towers ranged map cells
замыкаем список башен, начинаем отрисовывать башенку
ld a,b
call tower_screen_adr
view_tower_instatus
push hl
ld a,c
ld l,a
ld h,0
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl ; x 128
ld de,tower_gfx
add hl,de
pop de
push de
ld a,d
xor #80
ld b,a
ld c,e
отрисовка в 2 экрана сразу
dup 7
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
inc e
inc c
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
dec c
dec e
inc d
inc b
edup
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
inc e
inc c
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
dec c
dec e
call incd
ld a,d
xor #80
ld b,a
ld c,e
dup 8
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
inc e
inc c
ld a,(hl)
ld (de),a
ld (bc),a
inc hl
dec c
dec e
inc d
inc b
edup
pop hl
; attribute
;=========scr adr -> attr adr========
;in: DE - screen adress
;out:DE - attr adress
ld a,h
RRCA
RRCA
RRCA
AND 3
OR #58
LD h,a
or #80
ld d,a
ld e,l
ld bc,#1f
push hl
push de
ld a,#46
ld (hl),a
ld (de),a
inc l
inc e
ld (hl),a
ld (de),a
add hl,bc
ld e,l ;!!!! attr
ld (hl),a
ld (de),a
inc e
inc l
ld a,#44
ld (hl),a
ld (de),a
pop de
pop hl
ret
Когда игра уже была закончена, я понял, что алгоритм стрельбы башен мог быть другим.
Сейчас по каждому крипу перебираются все башни. Можно было-бы смотреть на каждую башню, перебирая всех крипов.
Но, хорошенько подумав, я понял, что такое изменение:
- прямо повлияет на баланс карт
- имеет дополнительный плюс как особенность игры, так как башни МОГУТ стрелять чаще при большом напоре крипов :)
11 комментариев
мышь не забудь включить :))
И встал вопрос? Если вби такой умный, почему он не пишет красивые эффекты в трд демы? Почему имея все признаки рст7, он им не стал? Вот это основа всего…
Тасм нет, потому что я не учился в школе как вы июне знаю математики и геометрии. Я даже 6 на 2 на калькуляторе умножаю.
Пойми, мы говорим про код чтобы писать лучше код. Мы говорим про демы для того чтобы делать лучше демы. Усидчивость — важное качество для кодера, но далеко не единственное.