PoP roomtest: ИИ стража — подход к Киду и боевые ветки диспетчера
Пункт 2 плана закрыт: страж не только замечает Кида, но и идёт к нему и дерётся. Порт по SDLPoP, диспетчер общий — ИИ выставляет те же control_*, что и клавиатура игрока. guards.c (банк 1), порт seg002: - autocontrol_guard_active (737) + kid_in_sight (0A93) + kid_armed (0AC1) + kid_far (09CB); - guard_advance / guard_block / guard_strike с таблицами вероятностей по 12 градациям мастерства (seg002:26..38), бросок prob > prandom(255); - move_2_backward / move_3_up / move_6_shift / move_down_back; - таймеры justblocked / kid_sword_strike / guard_refrac убывают раз в кадр в autocontrol_opponent, как в оригинале. pop_ctrl.c, порт seg005: control_with_sword (964), swordfight (0CDB), sword_strike, parry, forward_with_sword, back_with_sword. Ветвление у Кида и у соперника разное — соперник блокирует только на кадре 152, Кид ещё и по 153 (и тогда последовательность прокручивается сразу). pop_guard.c: guard_skill из данных уровня (12 градаций, вне диапазона -> 3), HP по get_guard_hp (extrastrength[skill] + tbl_guard_hp[уровень]), собственный сид бросков pop_fight_seed — иначе перерисовка стены сбивала бы решения стража. char_opp_dist переехал из банка в pop_kid.c: он нужен по ОБЕ стороны банковой границы — и ИИ, и диспетчеру боёвки. Проверено в MAME (комната 3): страж проходит комнату, встаёт в дистанцию и машет мечом, позы меняются. Урона пока нет — HP-учёт и check_hurt следующим шагом, без них бой не заканчивается. Бюджет В БОЮ (175 кадров): 412 224 – 421 068 = 0.958–0.979 кадра. В покое было 400 800. Запас в худшем кадре ~9 000 — тесно, но в один кадр укладываемся; оптимизация отложена сознательно. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -355,10 +355,150 @@ static void control_guard_inactive(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* control_with_sword (seg005:964) — боёвка. Порт следующим шагом; пока
|
||||
* персонаж с обнажённым мечом просто держит стойку. */
|
||||
/* ---- Боёвка (seg005:964..) ---------------------------------------- *
|
||||
* Всё это ветки ОБЩЕГО диспетчера: у стража тот же control(), просто
|
||||
* control_* ему выставляет ИИ (autocontrol_*, guards.c), а не клавиатура. */
|
||||
|
||||
/* back_with_sword (seg005): шаг назад — только из стоек. */
|
||||
static void back_with_sword(void)
|
||||
{
|
||||
uint8_t f = Char.frame;
|
||||
if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD ||
|
||||
f == FRAME_171_STAND_WITH_SWORD) {
|
||||
control_backward = CONTROL_IGNORE; /* без авто-повтора */
|
||||
seqtbl_offset_char(SEQ_57_BACK_WITH_SWORD);
|
||||
}
|
||||
}
|
||||
|
||||
/* forward_with_sword (seg005): шаг вперёд; у стража своя последовательность. */
|
||||
static void forward_with_sword(void)
|
||||
{
|
||||
uint8_t f = Char.frame;
|
||||
if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD ||
|
||||
f == FRAME_171_STAND_WITH_SWORD) {
|
||||
control_forward = CONTROL_IGNORE;
|
||||
seqtbl_offset_char(Char.charid != CHARID_0_KID ? SEQ_56_GUARD_FORWARD
|
||||
: SEQ_86_FORWARD_WITH_SWORD);
|
||||
}
|
||||
}
|
||||
|
||||
/* sword_strike (seg005): выпад из стойки/шага либо добивание после парирования. */
|
||||
static void sword_strike(void)
|
||||
{
|
||||
uint8_t f = Char.frame;
|
||||
uint8_t seq_id;
|
||||
if (f == FRAME_157_WALK_WITH_SWORD || f == FRAME_158_STAND_WITH_SWORD ||
|
||||
f == FRAME_170_STAND_WITH_SWORD || f == FRAME_171_STAND_WITH_SWORD ||
|
||||
f == FRAME_165_WALK_WITH_SWORD) {
|
||||
seq_id = (Char.charid == CHARID_0_KID) ? SEQ_75_STRIKE : SEQ_58_GUARD_STRIKE;
|
||||
} else if (f == FRAME_150_PARRY || f == FRAME_161_PARRY) {
|
||||
seq_id = SEQ_66_STRIKE_AFTER_PARRY;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
control_shift2 = CONTROL_IGNORE;
|
||||
seqtbl_offset_char(seq_id);
|
||||
}
|
||||
|
||||
/* parry (seg005): блок. Условие сильно разное у Кида и у соперника —
|
||||
* соперник блокирует ТОЛЬКО на кадре 152 (второй кадр удара), а Кид ещё и
|
||||
* по 153 (и тогда последовательность прокручивается сразу). */
|
||||
static void parry(void)
|
||||
{
|
||||
uint8_t cf = Char.frame, of = Opp.frame;
|
||||
uint8_t seq_id = SEQ_62_PARRY;
|
||||
uint8_t do_play_seq = 0;
|
||||
|
||||
if (cf == FRAME_158_STAND_WITH_SWORD || cf == FRAME_170_STAND_WITH_SWORD ||
|
||||
cf == FRAME_171_STAND_WITH_SWORD || cf == FRAME_168_BACK ||
|
||||
cf == FRAME_165_WALK_WITH_SWORD) {
|
||||
if (pop_char_opp_dist() >= 32 && Char.charid != CHARID_0_KID) {
|
||||
back_with_sword();
|
||||
return;
|
||||
} else if (Char.charid == CHARID_0_KID) {
|
||||
if (of == FRAME_168_BACK) return;
|
||||
if (of != FRAME_151_STRIKE_1 && of != FRAME_152_STRIKE_2 &&
|
||||
of != FRAME_162_BLOCK_TO_STRIKE) {
|
||||
if (of == FRAME_153_STRIKE_3) do_play_seq = 1;
|
||||
}
|
||||
} else {
|
||||
if (of != FRAME_152_STRIKE_2) return;
|
||||
}
|
||||
} else {
|
||||
if (cf != FRAME_167_BLOCKED) return;
|
||||
seq_id = SEQ_61_PARRY_AFTER_STRIKE;
|
||||
}
|
||||
control_up = CONTROL_IGNORE;
|
||||
seqtbl_offset_char(seq_id);
|
||||
if (do_play_seq) play_seq();
|
||||
}
|
||||
|
||||
/* swordfight (seg005:0CDB) — разбор ввода в бою. Порядок веток важен:
|
||||
* удар старше «убрать меч», тот старше блока, блок старше шагов. */
|
||||
static void swordfight(void)
|
||||
{
|
||||
uint8_t f = Char.frame;
|
||||
uint8_t seq_id;
|
||||
|
||||
if (f == FRAME_161_PARRY && control_shift2 >= CONTROL_RELEASED) {
|
||||
seqtbl_offset_char(SEQ_57_BACK_WITH_SWORD); /* отскок после парирования */
|
||||
return;
|
||||
} else if (control_shift2 == CONTROL_HELD) {
|
||||
if (Char.charid == CHARID_0_KID) kid_sword_strike = 15;
|
||||
sword_strike();
|
||||
if (control_shift2 == CONTROL_IGNORE) return;
|
||||
}
|
||||
|
||||
if (control_down == CONTROL_HELD) {
|
||||
if (f == FRAME_158_STAND_WITH_SWORD || f == FRAME_170_STAND_WITH_SWORD ||
|
||||
f == FRAME_171_STAND_WITH_SWORD) {
|
||||
control_down = CONTROL_IGNORE;
|
||||
Char.sword = SWORD_0_SHEATHED;
|
||||
if (Char.charid == CHARID_0_KID) {
|
||||
/* TODO: offguard/holding_sword — вместе с боёвкой Кида. */
|
||||
guard_refrac = 9;
|
||||
seq_id = SEQ_93_PUT_SWORD_AWAY_FAST;
|
||||
} else if (Char.charid == CHARID_1_SHADOW) {
|
||||
seq_id = SEQ_92_PUT_SWORD_AWAY;
|
||||
} else {
|
||||
seq_id = SEQ_87_GUARD_BECOME_INACT; /* Кид ушёл из виду */
|
||||
}
|
||||
seqtbl_offset_char(seq_id);
|
||||
}
|
||||
} else if (control_up == CONTROL_HELD) {
|
||||
parry();
|
||||
} else if (control_forward == CONTROL_HELD) {
|
||||
forward_with_sword();
|
||||
} else if (control_backward == CONTROL_HELD) {
|
||||
back_with_sword();
|
||||
}
|
||||
}
|
||||
|
||||
/* control_with_sword (seg005:964): пока соперник виден и близко — бой;
|
||||
* иначе страж убирает меч, а Кид просто стоит. */
|
||||
static void control_with_sword(void)
|
||||
{
|
||||
if (Char.action >= 2) return; /* actions_2_hang_climb и выше */
|
||||
if (pop_tile_at(Char.curr_col, Char.curr_row) == 11 /* loose */ ||
|
||||
can_guard_see_kid >= 2) {
|
||||
int16_t d = pop_char_opp_dist();
|
||||
if (d < 90) {
|
||||
if (d >= 0) { swordfight(); return; }
|
||||
/* Соперник за спиной: далеко — развернуться, вплотную — драться. */
|
||||
if (d < -4) { seqtbl_offset_char(SEQ_60_TURN_WITH_SWORD); return; }
|
||||
swordfight();
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Не видит / далеко. */
|
||||
if (Char.charid < CHARID_2_GUARD) {
|
||||
if (Char.frame == FRAME_171_STAND_WITH_SWORD) {
|
||||
Char.sword = SWORD_0_SHEATHED;
|
||||
seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY); /* страж убит */
|
||||
}
|
||||
} else {
|
||||
swordfight();
|
||||
}
|
||||
}
|
||||
|
||||
/* control() — диспетчер по кадру (seg005.c:252) для АКТИВНОГО персонажа. */
|
||||
|
||||
Reference in New Issue
Block a user