(бухай): программируй
148 subscribers
465 photos
96 videos
3 files
65 links
Мысли, дегустация и код
Download Telegram
И за два вечера справился с проблемой.
Решил пойти по сложному пути. И пусть это потребовало лишних 768 байт, но зато выглядит вроде неплохо. Тем более, что у меня есть еще больше 18 Кб свободных. И это довольно много.
Но как же я задолбался подстраивать текстуры для их отрисовки на мини-карте (для этого и потребовалась память и новые таблицы). Текстуры лежат в памяти в кривом виде для более быстрого рейкастинга и не очень подходят для отрисовки в обычном виде, поэтому пришлось помучиться.

Карту можно двигать на WASD, и на ней рисуется значок игрока. А еще отображаются только те блоки, которые игрок уже видел (туман войны).
А если изменить текстуру на карте или убрать блок, то мини-карта тоже поменяется.

Ну и конечно при создании уровня можно отметить блоки, которые никогда не будут отображаться на мини-карте. Это я буду использовать для секретов.
❤‍🔥4👍2
После того как добавил мини-карту, вылез жесткий баг.
Некоторые текстуры в случайном порядке начинали съезжать на 1 пиксель вверх (верхняя линия не рисуется, а нижняя берется из следующей текстуры).
Опытным путем я выяснил, что некоторые байты в таблице указателей на текстуры почему-то иногда увеличиваются на 1. Но из-за чего они увеличиваются, было непонятно.
Хорошо, что в Vice есть замечательная возможность записать процесс работы компьютера и потом все это дело изучить в отладчике.
Я записал небольшую часть геймплея до момента смещения текстур. Нашел в памяти изменившийся адрес, поставил на него watchpoint и изучил запись.

В общем, по цепочке я выявил баг.
Оказалось, что дело в том самом "тумане войны".
Когда игрок переходит на следующий блок, перед ним считывается 9 блоков. И некоторые из этих блоков могут быть вне координат уровня.
Для рендеринга уровня пофиг, потому что он огорожен стенами (и я ничего не пишу в таблицу уровня), но не для мини-карты.
Сами блоки мини-карты хранятся в таблице 24*24. Есть другая таблица с указателями на строки мини-карты. Я беру блок в нужной строке со смещением по X и включаю первый бит (отображение на карте).
Но что, если блок находится за пределами карты?
Правильно, мы выходим за пределы таблицы указателей и включаем первый бит в совершенно другом месте.
Хорошо, что под раздачу попали только текстуры, и косяк было видно визуально...
🔥4❤‍🔥1
Media is too big
VIEW IN TELEGRAM
Немного помучился с Atari 2600 и наконец-то сделал движущийся спрайт, который даже в разные стороны смотрит при повороте:)
Интересно, были ли в те времена похожие адские консоли для программистов.
Потому вроде даже в прямых конкурентах: Intellivision и ColecoVision была экранная память...
👍2❤‍🔥1
В общем, что здесь происходит:)

Как я уже сказал, у объектов Atari нет понятия координат. Нельзя просто выставить спрайту X и Y. Вместо этого, когда луч доходит до нужной линии, а главное - нужной точки на линии, мы говорим: "Рисуй прямо сейчас".

С Y все понятно, хотя все равно непривычно, что Y идет от наибольшего значения к наименьшему и означает, сколько линий до конца экрана осталось.
👍2❤‍🔥1
А вот с X сложнее. Нам надо высчитать, сколько тактов займет у видеочипа пройти по линии именно столько пикселей, сколько нам надо. А после этого записать что-нибудь в регистры RESxx (для разных объектов), что сбросит спрайт до текущей позиции луча.

Проблема в том, что это надо делать в цикле, особенно, если предполагается, что объект будет двигаться. Но одна итерация цикла занимает аж 5 тактов. За это время луч успевает пройти 15 пикселей, что делает движения дерганными.

Но Atari это предусмотрели в своем стиле. Есть регистры HMxx. Запись в последние 4 бита смещает спрайт от -7 до +8 пикселей от текущей позиции.

То есть надо взять текущую координату X, поделить на 15. Получаем количество итераций (по 5 тактов каждая). А потом берем остаток от деления X на 15. Получаем количество пикселей, на которое надо сместить спрайт влево или вправо.

А потом записью в регистр HMOVE двигаем спрайт.
🔥3💊3❤‍🔥1
Нарисовал текстуры для второго уровня. Как-то получилось более объемно, чем в прошлый раз. Но надо смотреть, как они будут выглядеть в игре. И видимо надо перерисовать текстуры для первого уровня.

А еще на каждом уровне будет отсылка к какой-нибудь классике. Здесь попытался нарисовать какодемона, но в условиях трех цветов вышло так себе)
🔥6❤‍🔥1
Изучил я исходники Doom RPG и понял, что все не так, как мне бы хотелось.
Во-первых, вот эта формула высчитывает количество нанесенного урона:
calDmg = (((((((wpn->strMin << 8) + ((randStr * ((wpn->strMax - wpn->strMin) << 8)) >> 8)) * (strength / defense)) >> 8) * i) >> 8) * calWpnDmg) >> 8;

Commodore такое потянет, но с натяжкой. По моим подсчетам, тут числа могут выйти даже в 32 бита. А хотелось бы остаться в 8-ми, максимум в 16-ти.
Во-вторых, в Doom RPG используются дополнительные параметры (например, при каждом выстреле высчитывается мощность оружия в 16 битах, которая потом используется при расчете). Да, можно перенести как есть, но я не хочу все усложнять.

В общем, после сотен вариантов я пришел к такой формуле:
полученный урон = мин. урон оружия + (((случайный урон оружия)*атака)*(атака/защита врага))
смещаем результат на 4 бита вправо (делим на 4) и прибавляем 1 (для того, чтобы даже при очень большом разрыве прилетал урон в 1 пункт)
если для врага оружие смертельно, то смещаем биты на 2 влево (умножаем на 2)

В целом, получаются нормальные цифры, но они немного зависят от случайности. Минус в том, что придется писать функции деления и умножения, которые мне пока что ни разу не пригодились. И все это в 16-ти битах.

Но и это еще не все! Есть же еще два показателя: accuracy и dexterity. Они должны влиять на вероятность попадания (как и на вероятность уклониться от атаки и избежать боя).

А еще у врагов и игрока есть броня! То есть помимо базового урона придется еще высчитывать урон, который на себя принимает броня (скорее всего, это будут какие-нибудь фиксированные проценты).

Короче, ролевка для меня - пока что самая сложная часть игры. Как бы я ни любил RPG, писать систему - это та еще дичь=)
❤‍🔥2
А еще я понял, как сохранять текущий прогресс на дискету (правда для этого потребуется 6 Кб памяти).
Карту вместе с активированными триггерами, спрайтами и так далее, сохранить не сложно. Благо в KERNAL есть функция, которая за это отвечает.
То есть надо банально скопировать текущее состояние карты (которая изначально подгружается с дискеты и изменяется в процессе прохождения) в нужный файл.

Вопрос возникает с показателями игрока.
Вариант 1 — создать второй файл, где будут храниться только параметры игрока.
Вариант 2 — выделить отдельный участок памяти, в котором параметры игрока будут расположены перед данными карты (сложно, т.к. потребует нарушения логики кода).
Вариант 3 — использовать пароли и забить на состояние карты. Вышел из игры — начинаешь с начала карты.
Вариант 4 — вместо паролей — показатели сохраняются в отдельный файл, но начинаешь с начала уровня.

Суть в том, что на дискете ограниченное пространство. Уже сейчас выходит не более 17 уровней. А что будет дальше, с музыкой, новыми спрайтами и ролевой системой? Десять штук максимум?

Есть вариант, сжать уровни на дискете. Это будет неплохой вариант, потому что там много повторяющихся байт, но я никогда не имел дело с архиваторами, поэтому придется еще повозиться=)
❤‍🔥1🔥1
Это надо брать. Поставлю рядом со своей Atari 2600)
❤‍🔥32🔥2
Ироничная фотография :)
❤‍🔥2
И даже поучаствовал в турнире по Quake 3. Но так как никогда особо в эту игру не играл, то финал получился довольно предсказуемым...
❤‍🔥1
Подумал я еще над формулами для битвы и остановился на следующем:
Урон:
damage = (weapon min. strength + random weapon strength * int(player attack/enemy defence)) << 1

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

Попадание:
chance = (int(((player accuracy << 2) / enemy dexterity) * 128) >> 4)

Получается число, которое увеличивается (но не более 255 == 100%) при увеличении разрыва между этими двумя числами. Потом на основе случайного числа уже высчитывается фактическое попадание.

А еще написал функции для 16-ти битного деления и умножения. В целом, работают быстро, а так как эти операции потребуются 4 раза за ход, то вообще не страшно. Я даже хочу сделать небольшую случайную задержку перед ходом врага, как будто он думает.
❤‍🔥1
Опять немного поправил функцию для расчета урона и перенес ее в ассемблер. Получилась конечно та еще дичь, но вроде работает.
        ; ---------------------- CALCULATE DAMAGE ------------------------------
; weapon_min + (rand(weapon_min; weapon_max) * ((((player_attack << 4)/enemy_defence)*32)>>6))
; ----------------------------------------------------------------------
; rotate left player attack 4 times
lda fight_player_attack
sta rotate_inout
lda #0
sta rotate_inout+1
ldy #3
jsr rol_16bit
; divide player_attack by enemy_defence
lda rotate_inout
sta div_dividend
lda rotate_inout+1
sta div_dividend+1
lda fight_enemy_defence
sta div_divisor
lda #0
sta div_divisor+1
jsr divide_16bit
; multiply the previous result by 32
lda div_dividend
sta mult_f_number
lda div_dividend+1
sta mult_f_number+1
lda #32
sta mult_s_number
lda #0
sta mult_s_number+1
jsr mult_16bit
; rotate right the previous result 6 times
lda mult_result
sta rotate_inout
lda mult_result+1
sta rotate_inout+1
ldy #5
jsr ror_16bit
; get random weapon strength
jsr get_random_number
lda rnd_1
ldy curr_weapon_mod
jsr mod
clc
adc curr_weapon_add
; multiply random number by the previous result
sta mult_f_number
lda #0
sta mult_f_number+1
lda rotate_inout
sta mult_s_number
lda rotate_inout+1
sta mult_s_number+1
jsr mult_16bit
; add minimum weapon strength
lda mult_result
clc
adc curr_weapon_add
sta mult_result
lda mult_result+1
adc #0
sta mult_result+1
❤‍🔥1
А вообще возникла такая идея.
У игрока будет всего 4 возможных варианта в бою (не считая аптечки и брони):
— Атака - обычная атака;
— Защита - defence * 2;
— Уклонение - dexterity * 2;
— Побег - dexterity / 2;

А вот с врагами интереснее!
Будет три типа атаки:
— Обычная атака;
— Усиленная атака - attack * 2
— Хитрая атака - accuracy * 2
И также будут уклонение и защита, как и у игрока.

И игроку надо будет выбирать, как поступить дальше. Например, если враг использует усиленную атаку, логичным вариантом будет встать в защиту.
На экран будут выводиться подсказки, но думаю, что для каждого врага нужно будет придумать свою собственную подсказку, как в старых jrpg.

Хочется уже показать что-то кроме сплошных формул, но пока на экране ничего не происходит=(
❤‍🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Немного помучился вчера с боями и теперь есть, что показать=)

Пока работают только 3 кнопки: атака, защита и уклонение.
— При атаке урон высчитывается по уже известной формуле. При этом, 25% урона всегда идет на броню, а остаток на здоровье. Если урон броне перевалил за количество брони, то остаток урона прибавляется к урону здоровья. Возможно над этим надо еще подумать.
— При нажатии на "Защиту", показатель defence умножается на 2.
— Аналогично с "Уклонением". Но на 2 умножается dexterity.
— После хода игрока все показатели врага сбрасываются до дефолтных значений и vice versa.

А ну и еще немного перерисовал текстуры первого уровня, изменил сам уровень и начал добавлять сюжетный текст.

Когда-нибудь это закончится, а то 14 мая будет уже ровно полгода, как мне в голову пришла идея снова заняться игрой на коммодор...
🔥5❤‍🔥2