refactor(core): 모델 및 유틸리티 개선
- GameState 확장 - BalanceConstants 조정 - PqLogic, Roman 정리
This commit is contained in:
@@ -569,6 +569,42 @@ class Equipment {
|
||||
);
|
||||
}
|
||||
|
||||
/// 초보자 장비 세트 (모든 슬롯에 기본 방어구 지급)
|
||||
///
|
||||
/// 원본 PQ에서는 초기 장비가 없지만, 밸런스 개선을 위해
|
||||
/// Act 1 완료 전에도 기본 방어력을 제공.
|
||||
factory Equipment.withStarterGear() {
|
||||
return Equipment(
|
||||
items: [
|
||||
EquipmentItem.defaultWeapon(), // 0: 무기 (Keyboard)
|
||||
_starterArmor('Old Mouse', EquipmentSlot.shield, def: 2),
|
||||
_starterArmor('Cardboard Box', EquipmentSlot.helm, def: 1),
|
||||
_starterArmor('Worn T-Shirt', EquipmentSlot.hauberk, def: 3),
|
||||
_starterArmor('Rubber Band', EquipmentSlot.brassairts, def: 1),
|
||||
_starterArmor('Wristwatch', EquipmentSlot.vambraces, def: 1),
|
||||
_starterArmor('Fingerless Gloves', EquipmentSlot.gauntlets, def: 1),
|
||||
_starterArmor('Hoodie', EquipmentSlot.gambeson, def: 2),
|
||||
_starterArmor('Jeans', EquipmentSlot.cuisses, def: 2),
|
||||
_starterArmor('Knee Pads', EquipmentSlot.greaves, def: 1),
|
||||
_starterArmor('Sneakers', EquipmentSlot.sollerets, def: 1),
|
||||
],
|
||||
bestIndex: 0,
|
||||
);
|
||||
}
|
||||
|
||||
/// 초보자 방어구 생성 헬퍼
|
||||
static EquipmentItem _starterArmor(String name, EquipmentSlot slot,
|
||||
{required int def}) {
|
||||
return EquipmentItem(
|
||||
name: name,
|
||||
slot: slot,
|
||||
level: 1,
|
||||
weight: 1,
|
||||
stats: ItemStats(def: def),
|
||||
rarity: ItemRarity.common,
|
||||
);
|
||||
}
|
||||
|
||||
/// 레거시 문자열 기반 생성자 (세이브 파일 호환용)
|
||||
factory Equipment.fromStrings({
|
||||
required String weapon,
|
||||
|
||||
@@ -150,16 +150,24 @@ class MonsterBaseStats {
|
||||
/// 레벨 기반 기본 스탯 생성
|
||||
///
|
||||
/// HP: 50 + level * 20 + (level^2 / 5)
|
||||
/// ATK: 10 + level * 12 (장비 DEF 스케일링 대응)
|
||||
/// - 장비 DEF ≈ level * 16 (9개 방어구 합산)
|
||||
/// - 데미지 공식: ATK - DEF * 0.5 → 의미있는 피해를 위해 상향
|
||||
/// ATK: 레벨별 차등 적용
|
||||
/// - 레벨 1~5: 2 + level * 3 (초반 난이도 완화)
|
||||
/// - 레벨 6+: 3 + level * 4 (기존 공식)
|
||||
/// - DEF 감산율 0.5와 연동하여 밸런스 조정
|
||||
/// - 플레이어가 5~10회 공격 생존 가능하도록 설계
|
||||
/// DEF: 2 + level * 2
|
||||
/// EXP: 10 + level * 5
|
||||
/// GOLD: 5 + level * 3
|
||||
factory MonsterBaseStats.forLevel(int level) {
|
||||
// 레벨 1~5: 초반 난이도 완화 (2 + level * 3)
|
||||
// 레벨 1=5, 2=8, 3=11, 4=14, 5=17
|
||||
// 레벨 6+: 기존 공식 (3 + level * 4)
|
||||
// 레벨 6=27, 10=43, 20=83, 50=203, 100=403
|
||||
final atk = level <= 5 ? 2 + level * 3 : 3 + level * 4;
|
||||
|
||||
return MonsterBaseStats(
|
||||
hp: 50 + level * 20 + (level * level ~/ 5),
|
||||
atk: 10 + level * 12,
|
||||
atk: atk,
|
||||
def: 2 + level * 2,
|
||||
exp: 10 + level * 5,
|
||||
gold: 5 + level * 3,
|
||||
@@ -447,14 +455,14 @@ class ActMonsterLevel {
|
||||
class PlayerScaling {
|
||||
PlayerScaling._();
|
||||
|
||||
/// 레벨당 HP 증가량 (10 → 12 상향)
|
||||
static const int hpPerLevel = 12;
|
||||
/// 레벨당 HP 증가량 (12 → 18 상향, 생존율 개선)
|
||||
static const int hpPerLevel = 18;
|
||||
|
||||
/// 레벨당 MP 증가량 (5 → 6 상향)
|
||||
static const int mpPerLevel = 6;
|
||||
|
||||
/// CON당 HP 보너스 (5 → 6 상향)
|
||||
static const int hpPerCon = 6;
|
||||
/// CON당 HP 보너스 (6 → 10 상향, 체력 투자 효율 개선)
|
||||
static const int hpPerCon = 10;
|
||||
|
||||
/// INT당 MP 보너스 (3 → 4 상향)
|
||||
static const int mpPerInt = 4;
|
||||
|
||||
@@ -798,12 +798,15 @@ ActResult completeAct(int existingActCount) {
|
||||
: 3600;
|
||||
|
||||
final rewards = <RewardKind>[];
|
||||
// 프롤로그 완료 시(existingActCount=1)부터 장비 보상 지급
|
||||
// 원본: existingActCount > 2 (Act II 이후)
|
||||
// 수정: existingActCount >= 1 (프롤로그 완료 후)
|
||||
if (existingActCount >= 1) {
|
||||
rewards.add(RewardKind.equip);
|
||||
}
|
||||
if (existingActCount > 1) {
|
||||
rewards.add(RewardKind.item);
|
||||
}
|
||||
if (existingActCount > 2) {
|
||||
rewards.add(RewardKind.equip);
|
||||
}
|
||||
|
||||
return ActResult(
|
||||
actTitle: title,
|
||||
|
||||
@@ -13,58 +13,41 @@ const _romanMap = <String, int>{
|
||||
};
|
||||
|
||||
String intToRoman(int n) {
|
||||
if (n <= 0) return '';
|
||||
|
||||
final buffer = StringBuffer();
|
||||
void emit(int value, String numeral) {
|
||||
|
||||
// 로마 숫자 변환 테이블 (내림차순)
|
||||
const values = [
|
||||
(10000, 'T'),
|
||||
(9000, 'MT'),
|
||||
(5000, 'A'),
|
||||
(4000, 'MA'),
|
||||
(1000, 'M'),
|
||||
(900, 'CM'),
|
||||
(500, 'D'),
|
||||
(400, 'CD'),
|
||||
(100, 'C'),
|
||||
(90, 'XC'),
|
||||
(50, 'L'),
|
||||
(40, 'XL'),
|
||||
(10, 'X'),
|
||||
(9, 'IX'),
|
||||
(5, 'V'),
|
||||
(4, 'IV'),
|
||||
(1, 'I'),
|
||||
];
|
||||
|
||||
for (final (value, numeral) in values) {
|
||||
while (n >= value) {
|
||||
buffer.write(numeral);
|
||||
n -= value;
|
||||
}
|
||||
}
|
||||
|
||||
emit(10000, 'T');
|
||||
if (n >= 9000) {
|
||||
buffer.write('MT');
|
||||
n -= 9000;
|
||||
}
|
||||
if (n >= 5000) {
|
||||
buffer.write('A');
|
||||
n -= 5000;
|
||||
}
|
||||
if (n >= 4000) {
|
||||
buffer.write('MA');
|
||||
n -= 4000;
|
||||
}
|
||||
|
||||
emit(1000, 'M');
|
||||
_subtract(ref: n, target: 900, numeral: 'CM', buffer: buffer);
|
||||
_subtract(ref: n, target: 500, numeral: 'D', buffer: buffer);
|
||||
_subtract(ref: n, target: 400, numeral: 'CD', buffer: buffer);
|
||||
|
||||
emit(100, 'C');
|
||||
_subtract(ref: n, target: 90, numeral: 'XC', buffer: buffer);
|
||||
_subtract(ref: n, target: 50, numeral: 'L', buffer: buffer);
|
||||
_subtract(ref: n, target: 40, numeral: 'XL', buffer: buffer);
|
||||
|
||||
emit(10, 'X');
|
||||
_subtract(ref: n, target: 9, numeral: 'IX', buffer: buffer);
|
||||
_subtract(ref: n, target: 5, numeral: 'V', buffer: buffer);
|
||||
_subtract(ref: n, target: 4, numeral: 'IV', buffer: buffer);
|
||||
emit(1, 'I');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
void _subtract({
|
||||
required int ref,
|
||||
required int target,
|
||||
required String numeral,
|
||||
required StringBuffer buffer,
|
||||
}) {
|
||||
if (ref >= target) {
|
||||
buffer.write(numeral);
|
||||
ref -= target;
|
||||
}
|
||||
}
|
||||
|
||||
int romanToInt(String n) {
|
||||
var result = 0;
|
||||
var i = 0;
|
||||
|
||||
Reference in New Issue
Block a user