기본 콘텐츠로 건너뛰기

추천 가젯

[프로그래밍 이야기 6] 포터블 라이터기의 재발견: 웹소켓과 스크립트가 만든 현장 대응 파이프라인

 임베디드 장비나 FPGA 칩이 탑재된 하드웨어를 현장에서 다룰 때, 가장 고민스러운 부분은 펌웨어 라이팅 환경을 구축하는 일입니다. 공장 PC나 현장 컴퓨터에 무겁고 거대한 벤더 전용 프로그램(IDE)을 일일이 설치하고 환경을 설정하는 것은 지극히 비효율적입니다. 이때 가장 똑똑하고 유연한 접근법은 공장 PC에 무언가를 설치하려 애쓰는 대신, 엔지니어의 노트북 한 대를 즉시 '포터블(Portable) 라이터기'로 탈바꿈시켜 현장에 투입하는 것입니다. 노트북을 포터블 라이터기로 만드는 전략과 웹소켓 노트북을 이동식 포터블 라이터기로 활용하기 위해 거창한 프로그램을 깔 필요는 전혀 없습니다. 핵심은 펌웨어를 입히는 CLI(명령줄 인터페이스) 툴과, 이를 원격에서 실행해 줄 지극히 단순하고 가벼운 웹소켓(WebSocket) 서버 하나만 준비하는 것입니다. 중요한 점은 여기서 사용하는 웹소켓 프로그램이라는 것이 결코 복잡하거나 무거운 시스템이 아니라는 사실입니다. 단 몇 킬로바이트 수준의 경량 훅(Hook) 역할만 수행하는 웹소켓 서버를 노트북에 띄워두면, 공장 PC 환경을 오염시키지 않고도 노트북 한 대가 완벽한 이동식 라이팅 단말기로 변신합니다. 가장 가벼운 웹소켓 훅과 Shell 스크립트의 결합 이 시스템의 실체는 놀라울 정도로 가볍고 명확합니다. 노트북 내부에서는 하드웨어(.sof)를 구운 뒤 MCU 펌웨어(.elf)를 연달아 다운로드하는 아래의 Shell 스크립트가 실행의 중심을 잡습니다. // flasher.sh - FPGA 및 Nios II 펌웨어 연속 라이팅 스크립트 #!/bin/bash 1. FPGA Hardware (.sof) 구우기 #!/bin/bash echo "Hello World" echo "=== Programming FPGA Hardware ===" quartus_pgm -c "USB-Blaster [1-4]" -m JTAG -o "p;mcu.sof...

[리버싱 일기/리메이크] 랑그릿사 2의 마법 데미지 공식은 어떻게 설계되었는가?

필자는 최근 고전 명작 랑그릿사 2 PC판을 기드라로 분석하며, 마법 데미지가 계산되는 내부 로직을 추적했습니다. 각종 구조체와 변수명을 정리하고 나니, 90년대 게임 특유의 실용적이면서도 명쾌한 공식이 모습을 드러냈습니다.

1. 특정 마법의 개별 로직: 토네이도(Tornado)

가장 먼저 분석한 것은 범위 마법인 토네이도입니다. 코드상에서는 대상 유닛의 타입에 따른 보너스 판정이 가장 먼저 눈에 띕니다.

C
void Tornado(void)
{
    // 대상 유닛이 비병일 경우, 보너스 데미지 플래그 활성화
    if (ARMY2_UNITTYPE == FLYING_UNIT) {
        BonusSpellDamage = 1;
    }

    /* 계산 공식: ((레벨 / 2) + 시전자의 SP) * 2 */
    int damageValue = (((int)FORCE1_LV >> 1) + (int)FORCE1_SP) * 2;

    // 최종 데미지 적용 함수 호출 (마법 방어력 계산 포함)
    ApplySpellDamage_MDF(damageValue);
}

토네이도는 비병에게 치명적이라는 설정답게, BonusSpellDamage를 통해 추가 타격을 줍니다. 흥미로운 점은 기본 데미지가 (레벨 >> 1 + SP) * 2로 계산된다는 것인데, 여기서 SP는 유닛의 티어값(파이터=1, 로드=2 등)을 의미합니다.

2. 마법 방어력을 고려한 최종 데미지 산출

입력된 기초 데미지는 ApplySpellDamage_MDF 함수를 거치며 타겟의 마법 방어력(MDF)에 의해 가공됩니다.

C
void ApplySpellDamage_MDF(int damage)
{
    // 1. 마법 면역 상태 및 마법 방어력 체크
    if (is_magic_immune(FORCE2) || (100 - FORCE1_2_MDF == 0)) return;

    // 2. 최종 데미지 계산식
    // 보너스 데미지 + 아이템 보너스 + (기초 데미지 * 마방 관통률)
    int real_damage = BonusSpellDamage + SpellDamage_Item + 
                      (damage * (100 - (uint)FORCE1_2_MDF)) / 100;

    // 3. 데미지 보정 (데미지 하한선 및 HP 초과 방지)
    if (real_damage <= 0) real_damage = 1;

    int targetHP = (int)FORCE2->Army[ARMY2_NUMBER].HP;
    if (targetHP < real_damage) real_damage = targetHP;

    // 4. 누적 결과 기록 (범위 마법 계산용)
    AccumSpellEffect += real_damage;
    AccumSpellTarget += 1;

    // 5. 실행 플래그 확인 후 실제 데이터 반영
    if (_run_flag_apply == 1) {
        FORCE2->Army[ARMY2_NUMBER].HP -= (char)real_damage;
        
        if (FORCE2->Army[ARMY2_NUMBER].HP == 0) {
            killed_target_logic(); // 유닛 전사 처리
        }
    }
}

3. 분석을 통해 알 수 있는 사실들

분석 결과, 랑그릿사 2의 마법 데미지는 크게 세 단계를 거쳐 적용됩니다.

  1. 최소 데미지의 보장: 마법 방어력이 아무리 높더라도, 공식에 의해 산출된 데미지가 0이라면 강제로 1의 데미지를 가합니다.

  2. 누적 데이터의 활용: AccumSpellEffectAccumSpellTarget 변수를 통해 마법 한 번으로 총 얼마의 데미지를 몇 명에게 입혔는지 기록합니다. 이는 주로 범위 마법의 효율을 계산할 때 사용됩니다.

  3. AI 시뮬레이션의 분리: _run_flag_apply라는 플래그가 매우 중요합니다. AI가 마법을 쓸지 말지 결정하기 위해 데미지를 미리 계산해 볼 때는 이 플래그를 끄고 함수를 호출하여, 실제 HP는 깎지 않은 채 예상 결과물만 얻어가는 구조입니다.

맺으며

기드라에서 이 로직을 타고 올라가면, 적군 AI가 마법을 선택하는 판단 근거를 찾을 수 있을 것으로 보입니다. 비병에게 토네이도를 날리는 AI의 영리함은 결국 이런 간결한 if문과 시뮬레이션 플래그에서 시작된 것이었습니다. 리버싱은 단순한 코드 해독을 넘어, 30년 전 개발자의 설계 철학을 엿보는 즐거운 과정입니다.

댓글