Captain Drexx изнутри. Часть 4. Towers

Итак, крипы лезут. Нужно обороняться.
Содержание цикла «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 комментариев

avatar
Где поиграть? =)
avatar
avatar
zx.kaniv.net/ — tap & trd
avatar
Ооо, да это еще и TD! =)
avatar
приятной игры!
мышь не забудь включить :))
avatar
Вот прочитал статью вби, естественно мышкой вниз не читая, все равно ничего не понимаю.

И встал вопрос? Если вби такой умный, почему он не пишет красивые эффекты в трд демы? Почему имея все признаки рст7, он им не стал? Вот это основа всего…
  • AAA
  • 0
avatar
… сказал ярый фанат анимации в демах.
avatar
Я не фанат анимации. Я делаю демы как могу и на тех инструментах что мне понятны. С запиляторном я разбирался несколько часов. С тасмом годы!!! Я МГУ сделать дему в тасме сопоставив куски драного кода, аля гоблин. Но создать из головы принципиально новый эффект, с табличкой летания спрайта не смогу. Я делаю на запиляторе шедевры, этот пластилин позволяет мне самореализовываться на все 100% Ааа!

Тасм нет, потому что я не учился в школе как вы июне знаю математики и геометрии. Я даже 6 на 2 на калькуляторе умножаю.
avatar
ААА, ты всё же несправедлив. Почему ты думаешь, что если эта статья была неинтересна тебе, она сразу же автоматически не интересна всем остальным? И хотя я, в общем, согласен, что шутку про кодеров в рабстве ты придумал довольно смешную, но ты повторил её уже столько раз, что уже давно не смешно, если честно.

Пойми, мы говорим про код чтобы писать лучше код. Мы говорим про демы для того чтобы делать лучше демы. Усидчивость — важное качество для кодера, но далеко не единственное.
avatar
Если бы я был вумный как вби написал бы такойю игру:

  • AAA
  • 0
avatar
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.