diff --git a/documentation/DocumentationTest.MonsterStatDocumentation.approved.md b/documentation/DocumentationTest.MonsterStatDocumentation.approved.md
new file mode 100644
index 0000000..10d62fb
--- /dev/null
+++ b/documentation/DocumentationTest.MonsterStatDocumentation.approved.md
@@ -0,0 +1,145 @@
+# Monster Stat Table
+
+## Race 0
+- Level 1 - HP: 156 - MP: 10 - Defence: 18/17/13 - Dodge: 31 - Melee: 32~40 hit 27 - Ranged: 27~35 hit 34 - Magic: 22~30 hit 70
+- Level 10 - HP: 363 - MP: 69 - Defence: 41/43/35 - Dodge: 76 - Melee: 69~93 hit 72 - Ranged: 64~88 hit 88 - Magic: 59~83 hit 70
+- Level 20 - HP: 688 - MP: 181 - Defence: 67/72/61 - Dodge: 126 - Melee: 111~151 hit 122 - Ranged: 106~146 hit 148 - Magic: 101~141 hit 70
+- Level 30 - HP: 1113 - MP: 344 - Defence: 92/100/86 - Dodge: 176 - Melee: 153~209 hit 172 - Ranged: 148~204 hit 208 - Magic: 143~199 hit 70
+- Level 37 - HP: 1764 - MP: 487 - Defence: 111/120/104 - Dodge: 211 - Melee: 183~249 hit 207 - Ranged: 178~244 hit 250 - Magic: 173~239 hit 70
+- Level 40 - HP: 1965 - MP: 556 - Defence: 118/129/111 - Dodge: 226 - Melee: 195~267 hit 222 - Ranged: 190~262 hit 268 - Magic: 185~257 hit 70
+- Level 50 - HP: 2715 - MP: 819 - Defence: 144/157/137 - Dodge: 276 - Melee: 237~325 hit 272 - Ranged: 232~320 hit 328 - Magic: 227~315 hit 70
+- Level 52 - HP: 3600 - MP: 877 - Defence: 149/163/142 - Dodge: 286 - Melee: 246~336 hit 282 - Ranged: 241~331 hit 340 - Magic: 236~326 hit 70
+- Level 60 - HP: 4482 - MP: 1131 - Defence: 170/186/162 - Dodge: 326 - Melee: 279~383 hit 322 - Ranged: 274~378 hit 388 - Magic: 269~373 hit 70
+- Level 62 - HP: 5661 - MP: 1200 - Defence: 175/192/167 - Dodge: 336 - Melee: 288~394 hit 332 - Ranged: 283~389 hit 400 - Magic: 278~384 hit 70
+- Level 70 - HP: 6863 - MP: 1494 - Defence: 196/215/188 - Dodge: 376 - Melee: 321~441 hit 372 - Ranged: 316~436 hit 448 - Magic: 311~431 hit 70
+- Level 72 - HP: 9975 - MP: 1572 - Defence: 201/220/193 - Dodge: 386 - Melee: 330~452 hit 382 - Ranged: 325~447 hit 460 - Magic: 320~442 hit 70
+- Level 80 - HP: 11845 - MP: 1906 - Defence: 221/243/213 - Dodge: 426 - Melee: 363~499 hit 422 - Ranged: 358~494 hit 508 - Magic: 353~489 hit 70
+- Level 82 - HP: 17272 - MP: 1995 - Defence: 226/249/218 - Dodge: 436 - Melee: 372~510 hit 432 - Ranged: 367~505 hit 520 - Magic: 362~500 hit 70
+- Level 90 - HP: 20170 - MP: 2369 - Defence: 247/272/238 - Dodge: 476 - Melee: 405~557 hit 472 - Ranged: 400~552 hit 568 - Magic: 395~547 hit 70
+- Level 99 - HP: 23698 - MP: 2828 - Defence: 271/297/262 - Dodge: 521 - Melee: 443~609 hit 517 - Ranged: 438~604 hit 622 - Magic: 433~599 hit 70
+
+## Race 1
+- Level 1 - HP: 636 - MP: 192 - Defence: 22/21/21 - Dodge: 39 - Melee: 40~48 hit 35 - Ranged: 35~43 hit 50 - Magic: 38~46 hit 70
+- Level 10 - HP: 1275 - MP: 494 - Defence: 50/51/48 - Dodge: 93 - Melee: 86~110 hit 89 - Ranged: 81~105 hit 122 - Magic: 84~108 hit 70
+- Level 20 - HP: 2365 - MP: 1019 - Defence: 81/85/79 - Dodge: 153 - Melee: 138~178 hit 149 - Ranged: 133~173 hit 202 - Magic: 136~176 hit 70
+- Level 30 - HP: 3855 - MP: 1744 - Defence: 111/118/109 - Dodge: 213 - Melee: 190~246 hit 209 - Ranged: 185~241 hit 282 - Magic: 188~244 hit 70
+- Level 37 - HP: 6163 - MP: 2370 - Defence: 133/142/130 - Dodge: 255 - Melee: 227~293 hit 251 - Ranged: 222~288 hit 338 - Magic: 225~291 hit 70
+- Level 40 - HP: 6894 - MP: 2669 - Defence: 142/152/139 - Dodge: 273 - Melee: 242~314 hit 269 - Ranged: 237~309 hit 362 - Magic: 240~312 hit 70
+- Level 50 - HP: 9642 - MP: 3794 - Defence: 173/185/170 - Dodge: 333 - Melee: 294~382 hit 329 - Ranged: 289~377 hit 442 - Magic: 292~380 hit 70
+- Level 52 - HP: 12811 - MP: 4043 - Defence: 179/192/176 - Dodge: 345 - Melee: 305~395 hit 341 - Ranged: 300~390 hit 458 - Magic: 303~393 hit 70
+- Level 60 - HP: 16087 - MP: 5119 - Defence: 204/219/200 - Dodge: 393 - Melee: 346~450 hit 389 - Ranged: 341~445 hit 522 - Magic: 344~448 hit 70
+- Level 62 - HP: 20359 - MP: 5408 - Defence: 210/226/206 - Dodge: 405 - Melee: 357~463 hit 401 - Ranged: 352~458 hit 538 - Magic: 355~461 hit 70
+- Level 70 - HP: 24867 - MP: 6644 - Defence: 235/253/231 - Dodge: 453 - Melee: 398~518 hit 449 - Ranged: 393~513 hit 602 - Magic: 396~516 hit 70
+- Level 72 - HP: 36202 - MP: 6973 - Defence: 241/259/237 - Dodge: 465 - Melee: 409~531 hit 461 - Ranged: 404~526 hit 618 - Magic: 407~529 hit 70
+- Level 80 - HP: 43262 - MP: 8369 - Defence: 265/286/261 - Dodge: 513 - Melee: 450~586 hit 509 - Ranged: 445~581 hit 682 - Magic: 448~584 hit 70
+- Level 82 - HP: 63178 - MP: 8738 - Defence: 271/293/267 - Dodge: 525 - Melee: 461~599 hit 521 - Ranged: 456~594 hit 698 - Magic: 459~597 hit 70
+- Level 90 - HP: 74182 - MP: 10294 - Defence: 296/320/291 - Dodge: 573 - Melee: 502~654 hit 569 - Ranged: 497~649 hit 762 - Magic: 500~652 hit 70
+- Level 99 - HP: 87633 - MP: 12197 - Defence: 324/350/319 - Dodge: 627 - Melee: 549~715 hit 623 - Ranged: 544~710 hit 834 - Magic: 547~713 hit 70
+
+## Race 2
+- Level 1 - HP: 121 - MP: 60 - Defence: 17/19/17 - Dodge: 34 - Melee: 30~38 hit 30 - Ranged: 30~38 hit 40 - Magic: 30~38 hit 70
+- Level 10 - HP: 420 - MP: 130 - Defence: 42/44/38 - Dodge: 79 - Melee: 71~95 hit 75 - Ranged: 67~91 hit 94 - Magic: 65~89 hit 70
+- Level 20 - HP: 975 - MP: 240 - Defence: 71/73/63 - Dodge: 129 - Melee: 118~158 hit 125 - Ranged: 109~149 hit 154 - Magic: 105~145 hit 70
+- Level 30 - HP: 1755 - MP: 382 - Defence: 98/101/87 - Dodge: 179 - Melee: 165~221 hit 175 - Ranged: 151~207 hit 214 - Magic: 145~201 hit 70
+- Level 37 - HP: 2964 - MP: 487 - Defence: 119/122/104 - Dodge: 214 - Melee: 199~265 hit 210 - Ranged: 181~247 hit 256 - Magic: 173~239 hit 70
+- Level 40 - HP: 3312 - MP: 556 - Defence: 127/130/111 - Dodge: 229 - Melee: 212~284 hit 225 - Ranged: 193~265 hit 274 - Magic: 185~257 hit 70
+- Level 50 - HP: 4788 - MP: 762 - Defence: 155/158/136 - Dodge: 279 - Melee: 259~347 hit 275 - Ranged: 235~323 hit 334 - Magic: 225~313 hit 70
+- Level 52 - HP: 6394 - MP: 790 - Defence: 161/164/141 - Dodge: 289 - Melee: 269~359 hit 285 - Ranged: 244~334 hit 346 - Magic: 233~323 hit 70
+- Level 60 - HP: 8167 - MP: 1000 - Defence: 184/187/160 - Dodge: 329 - Melee: 306~410 hit 325 - Ranged: 277~381 hit 394 - Magic: 265~369 hit 70
+- Level 62 - HP: 10373 - MP: 1032 - Defence: 189/193/165 - Dodge: 339 - Melee: 316~422 hit 335 - Ranged: 286~392 hit 406 - Magic: 273~379 hit 70
+- Level 70 - HP: 12825 - MP: 1270 - Defence: 212/216/185 - Dodge: 379 - Melee: 353~473 hit 375 - Ranged: 319~439 hit 454 - Magic: 305~425 hit 70
+- Level 72 - HP: 18720 - MP: 1306 - Defence: 218/221/190 - Dodge: 389 - Melee: 363~485 hit 385 - Ranged: 328~450 hit 466 - Magic: 313~435 hit 70
+- Level 80 - HP: 22575 - MP: 1572 - Defence: 240/244/209 - Dodge: 429 - Melee: 400~536 hit 425 - Ranged: 361~497 hit 514 - Magic: 345~481 hit 70
+- Level 82 - HP: 33033 - MP: 1612 - Defence: 245/250/214 - Dodge: 439 - Melee: 410~548 hit 435 - Ranged: 370~508 hit 526 - Magic: 353~491 hit 70
+- Level 90 - HP: 39060 - MP: 1906 - Defence: 268/273/233 - Dodge: 479 - Melee: 447~599 hit 475 - Ranged: 403~555 hit 574 - Magic: 385~537 hit 70
+- Level 99 - HP: 46728 - MP: 2225 - Defence: 294/299/256 - Dodge: 524 - Melee: 490~656 hit 520 - Ranged: 441~607 hit 628 - Magic: 421~587 hit 70
+
+## Race 3
+- Level 1 - HP: 221 - MP: 60 - Defence: 17/19/17 - Dodge: 34 - Melee: 30~38 hit 30 - Ranged: 30~38 hit 40 - Magic: 30~38 hit 70
+- Level 10 - HP: 410 - MP: 155 - Defence: 40/44/39 - Dodge: 79 - Melee: 67~91 hit 75 - Ranged: 67~91 hit 94 - Magic: 67~91 hit 70
+- Level 20 - HP: 715 - MP: 307 - Defence: 66/73/65 - Dodge: 129 - Melee: 109~149 hit 125 - Ranged: 109~149 hit 154 - Magic: 109~149 hit 70
+- Level 30 - HP: 1120 - MP: 510 - Defence: 91/101/90 - Dodge: 179 - Melee: 151~207 hit 175 - Ranged: 151~207 hit 214 - Magic: 151~207 hit 70
+- Level 37 - HP: 1755 - MP: 681 - Defence: 110/122/108 - Dodge: 214 - Melee: 181~247 hit 210 - Ranged: 181~247 hit 256 - Magic: 181~247 hit 70
+- Level 40 - HP: 1950 - MP: 762 - Defence: 117/130/115 - Dodge: 229 - Melee: 193~265 hit 225 - Ranged: 193~265 hit 274 - Magic: 193~265 hit 70
+- Level 50 - HP: 2676 - MP: 1065 - Defence: 143/158/141 - Dodge: 279 - Melee: 235~323 hit 275 - Ranged: 235~323 hit 334 - Magic: 235~323 hit 70
+- Level 52 - HP: 3544 - MP: 1131 - Defence: 148/164/146 - Dodge: 289 - Melee: 244~334 hit 285 - Ranged: 244~334 hit 346 - Magic: 244~334 hit 70
+- Level 60 - HP: 4402 - MP: 1417 - Defence: 169/187/166 - Dodge: 329 - Melee: 277~381 hit 325 - Ranged: 277~381 hit 394 - Magic: 277~381 hit 70
+- Level 62 - HP: 5558 - MP: 1494 - Defence: 174/193/171 - Dodge: 339 - Melee: 286~392 hit 335 - Ranged: 286~392 hit 406 - Magic: 286~392 hit 70
+- Level 70 - HP: 6732 - MP: 1820 - Defence: 195/216/192 - Dodge: 379 - Melee: 319~439 hit 375 - Ranged: 319~439 hit 454 - Magic: 319~439 hit 70
+- Level 72 - HP: 9782 - MP: 1906 - Defence: 200/221/197 - Dodge: 389 - Melee: 328~450 hit 385 - Ranged: 328~450 hit 466 - Magic: 328~450 hit 70
+- Level 80 - HP: 11612 - MP: 2272 - Defence: 220/244/217 - Dodge: 429 - Melee: 361~497 hit 425 - Ranged: 361~497 hit 514 - Magic: 361~497 hit 70
+- Level 82 - HP: 16933 - MP: 2369 - Defence: 225/250/222 - Dodge: 439 - Melee: 370~508 hit 435 - Ranged: 370~508 hit 526 - Magic: 370~508 hit 70
+- Level 90 - HP: 19775 - MP: 2775 - Defence: 246/273/242 - Dodge: 479 - Melee: 403~555 hit 475 - Ranged: 403~555 hit 574 - Magic: 403~555 hit 70
+- Level 99 - HP: 23240 - MP: 3270 - Defence: 270/299/266 - Dodge: 524 - Melee: 441~607 hit 520 - Ranged: 441~607 hit 628 - Magic: 441~607 hit 70
+
+## Race 4
+- Level 1 - HP: 716 - MP: 408 - Defence: 19/21/19 - Dodge: 39 - Melee: 35~43 hit 35 - Ranged: 35~43 hit 50 - Magic: 35~43 hit 70
+- Level 10 - HP: 981 - MP: 682 - Defence: 43/47/46 - Dodge: 85 - Melee: 73~97 hit 81 - Ranged: 73~97 hit 106 - Magic: 81~105 hit 70
+- Level 20 - HP: 1431 - MP: 1177 - Defence: 70/77/77 - Dodge: 137 - Melee: 117~157 hit 133 - Ranged: 117~157 hit 170 - Magic: 133~173 hit 70
+- Level 30 - HP: 2025 - MP: 1872 - Defence: 96/106/107 - Dodge: 189 - Melee: 161~217 hit 185 - Ranged: 161~217 hit 234 - Magic: 185~241 hit 70
+- Level 37 - HP: 3078 - MP: 2478 - Defence: 116/128/128 - Dodge: 226 - Melee: 193~259 hit 222 - Ranged: 193~259 hit 280 - Magic: 222~288 hit 70
+- Level 40 - HP: 3315 - MP: 2767 - Defence: 123/136/137 - Dodge: 241 - Melee: 205~277 hit 237 - Ranged: 205~277 hit 298 - Magic: 237~309 hit 70
+- Level 50 - HP: 4374 - MP: 3862 - Defence: 150/165/168 - Dodge: 293 - Melee: 249~337 hit 289 - Ranged: 249~337 hit 362 - Magic: 289~377 hit 70
+- Level 52 - HP: 5832 - MP: 4105 - Defence: 156/172/174 - Dodge: 304 - Melee: 259~349 hit 300 - Ranged: 259~349 hit 376 - Magic: 300~390 hit 70
+- Level 60 - HP: 7006 - MP: 5157 - Defence: 177/195/198 - Dodge: 345 - Melee: 293~397 hit 341 - Ranged: 293~397 hit 426 - Magic: 341~445 hit 70
+- Level 62 - HP: 8910 - MP: 5440 - Defence: 183/202/204 - Dodge: 356 - Melee: 303~409 hit 352 - Ranged: 303~409 hit 440 - Magic: 352~458 hit 70
+- Level 70 - HP: 10513 - MP: 6652 - Defence: 204/225/229 - Dodge: 397 - Melee: 337~457 hit 393 - Ranged: 337~457 hit 490 - Magic: 393~513 hit 70
+- Level 72 - HP: 15390 - MP: 6975 - Defence: 210/231/235 - Dodge: 408 - Melee: 347~469 hit 404 - Ranged: 347~469 hit 504 - Magic: 404~526 hit 70
+- Level 80 - HP: 17887 - MP: 8347 - Defence: 230/254/259 - Dodge: 449 - Melee: 381~517 hit 445 - Ranged: 381~517 hit 554 - Magic: 445~581 hit 70
+- Level 82 - HP: 26271 - MP: 8710 - Defence: 236/261/265 - Dodge: 460 - Melee: 391~529 hit 456 - Ranged: 391~529 hit 568 - Magic: 456~594 hit 70
+- Level 90 - HP: 30145 - MP: 10242 - Defence: 257/284/289 - Dodge: 501 - Melee: 425~577 hit 497 - Ranged: 425~577 hit 618 - Magic: 497~649 hit 70
+- Level 99 - HP: 35266 - MP: 12119 - Defence: 282/311/317 - Dodge: 548 - Melee: 465~631 hit 544 - Ranged: 465~631 hit 676 - Magic: 544~710 hit 70
+
+## Race 5
+- Level 1 - HP: 276 - MP: 38 - Defence: 15/17/15 - Dodge: 31 - Melee: 27~35 hit 27 - Ranged: 27~35 hit 34 - Magic: 27~35 hit 70
+- Level 10 - HP: 395 - MP: 97 - Defence: 38/42/37 - Dodge: 74 - Melee: 62~86 hit 70 - Ranged: 62~86 hit 84 - Magic: 62~86 hit 70
+- Level 20 - HP: 591 - MP: 195 - Defence: 63/70/62 - Dodge: 122 - Melee: 102~142 hit 118 - Ranged: 102~142 hit 140 - Magic: 102~142 hit 70
+- Level 30 - HP: 851 - MP: 325 - Defence: 87/97/86 - Dodge: 170 - Melee: 142~198 hit 166 - Ranged: 142~198 hit 196 - Magic: 142~198 hit 70
+- Level 37 - HP: 1255 - MP: 423 - Defence: 104/116/102 - Dodge: 203 - Melee: 170~236 hit 199 - Ranged: 170~236 hit 234 - Magic: 170~236 hit 70
+- Level 40 - HP: 1410 - MP: 487 - Defence: 112/125/110 - Dodge: 218 - Melee: 182~254 hit 214 - Ranged: 182~254 hit 252 - Magic: 182~254 hit 70
+- Level 50 - HP: 1875 - MP: 681 - Defence: 137/152/135 - Dodge: 266 - Melee: 222~310 hit 262 - Ranged: 222~310 hit 308 - Magic: 222~310 hit 70
+- Level 52 - HP: 2424 - MP: 708 - Defence: 141/157/139 - Dodge: 275 - Melee: 230~320 hit 271 - Ranged: 230~320 hit 318 - Magic: 230~320 hit 70
+- Level 60 - HP: 3022 - MP: 907 - Defence: 162/180/159 - Dodge: 314 - Melee: 262~366 hit 310 - Ranged: 262~366 hit 364 - Magic: 262~366 hit 70
+- Level 62 - HP: 3736 - MP: 938 - Defence: 166/185/163 - Dodge: 323 - Melee: 270~376 hit 319 - Ranged: 270~376 hit 374 - Magic: 270~376 hit 70
+- Level 70 - HP: 4555 - MP: 1165 - Defence: 187/208/184 - Dodge: 362 - Melee: 302~422 hit 358 - Ranged: 302~422 hit 420 - Magic: 302~422 hit 70
+- Level 72 - HP: 6500 - MP: 1200 - Defence: 191/212/188 - Dodge: 371 - Melee: 310~432 hit 367 - Ranged: 310~432 hit 430 - Magic: 310~432 hit 70
+- Level 80 - HP: 7777 - MP: 1455 - Defence: 211/235/208 - Dodge: 410 - Melee: 342~478 hit 406 - Ranged: 342~478 hit 476 - Magic: 342~478 hit 70
+- Level 82 - HP: 11158 - MP: 1494 - Defence: 215/240/212 - Dodge: 419 - Melee: 350~488 hit 415 - Ranged: 350~488 hit 486 - Magic: 350~488 hit 70
+- Level 90 - HP: 13142 - MP: 1777 - Defence: 236/263/232 - Dodge: 458 - Melee: 382~534 hit 454 - Ranged: 382~534 hit 532 - Magic: 382~534 hit 70
+- Level 99 - HP: 15298 - MP: 2085 - Defence: 258/287/254 - Dodge: 501 - Melee: 418~584 hit 497 - Ranged: 418~584 hit 582 - Magic: 418~584 hit 70
+
+## Race 6
+- Level 1 - HP: 30 - MP: 725 - Defence: 13/19/27 - Dodge: 34 - Melee: 23~31 hit 30 - Ranged: 30~38 hit 40 - Magic: 50~58 hit 70
+- Level 10 - HP: 156 - MP: 1005 - Defence: 37/44/51 - Dodge: 79 - Melee: 60~84 hit 75 - Ranged: 67~91 hit 94 - Magic: 91~115 hit 70
+- Level 20 - HP: 391 - MP: 1432 - Defence: 63/73/80 - Dodge: 129 - Melee: 102~142 hit 125 - Ranged: 109~149 hit 154 - Magic: 138~178 hit 70
+- Level 30 - HP: 726 - MP: 1972 - Defence: 88/101/107 - Dodge: 179 - Melee: 144~200 hit 175 - Ranged: 151~207 hit 214 - Magic: 185~241 hit 70
+- Level 37 - HP: 1224 - MP: 2440 - Defence: 106/122/127 - Dodge: 214 - Melee: 174~240 hit 210 - Ranged: 181~247 hit 256 - Magic: 219~285 hit 70
+- Level 40 - HP: 1393 - MP: 2625 - Defence: 114/130/135 - Dodge: 229 - Melee: 186~258 hit 225 - Ranged: 193~265 hit 274 - Magic: 232~304 hit 70
+- Level 50 - HP: 2035 - MP: 3390 - Defence: 140/158/163 - Dodge: 279 - Melee: 228~316 hit 275 - Ranged: 235~323 hit 334 - Magic: 279~367 hit 70
+- Level 52 - HP: 2722 - MP: 3556 - Defence: 145/164/169 - Dodge: 289 - Melee: 237~327 hit 285 - Ranged: 244~334 hit 346 - Magic: 289~379 hit 70
+- Level 60 - HP: 3496 - MP: 4267 - Defence: 166/187/191 - Dodge: 329 - Melee: 270~374 hit 325 - Ranged: 277~381 hit 394 - Magic: 326~430 hit 70
+- Level 62 - HP: 4446 - MP: 4456 - Defence: 171/193/196 - Dodge: 339 - Melee: 279~385 hit 335 - Ranged: 286~392 hit 406 - Magic: 336~442 hit 70
+- Level 70 - HP: 5518 - MP: 5257 - Defence: 192/216/219 - Dodge: 379 - Melee: 312~432 hit 375 - Ranged: 319~439 hit 454 - Magic: 373~493 hit 70
+- Level 72 - HP: 8062 - MP: 5469 - Defence: 197/221/225 - Dodge: 389 - Melee: 321~443 hit 385 - Ranged: 328~450 hit 466 - Magic: 383~505 hit 70
+- Level 80 - HP: 9752 - MP: 6360 - Defence: 217/244/247 - Dodge: 429 - Melee: 354~490 hit 425 - Ranged: 361~497 hit 514 - Magic: 420~556 hit 70
+- Level 82 - HP: 14280 - MP: 6594 - Defence: 222/250/252 - Dodge: 439 - Melee: 363~501 hit 435 - Ranged: 370~508 hit 526 - Magic: 430~568 hit 70
+- Level 90 - HP: 16926 - MP: 7575 - Defence: 243/273/274 - Dodge: 479 - Melee: 396~548 hit 475 - Ranged: 403~555 hit 574 - Magic: 467~619 hit 70
+- Level 99 - HP: 20170 - MP: 8810 - Defence: 266/299/300 - Dodge: 524 - Melee: 434~600 hit 520 - Ranged: 441~607 hit 628 - Magic: 510~676 hit 70
+
+## Race 8
+- Level 1 - HP: 7 - MP: 4 - Defence: 12/14/12 - Dodge: 24 - Melee: 20~28 hit 20 - Ranged: 20~28 hit 20 - Magic: 20~28 hit 70
+- Level 10 - HP: 7 - MP: 4 - Defence: 31/35/30 - Dodge: 60 - Melee: 48~72 hit 56 - Ranged: 48~72 hit 56 - Magic: 48~72 hit 70
+- Level 20 - HP: 7 - MP: 4 - Defence: 52/59/51 - Dodge: 100 - Melee: 80~120 hit 96 - Ranged: 80~120 hit 96 - Magic: 80~120 hit 70
+- Level 30 - HP: 7 - MP: 4 - Defence: 72/82/71 - Dodge: 140 - Melee: 112~168 hit 136 - Ranged: 112~168 hit 136 - Magic: 112~168 hit 70
+- Level 37 - HP: 8 - MP: 4 - Defence: 87/99/85 - Dodge: 168 - Melee: 135~201 hit 164 - Ranged: 135~201 hit 164 - Magic: 135~201 hit 70
+- Level 40 - HP: 8 - MP: 4 - Defence: 93/106/91 - Dodge: 180 - Melee: 144~216 hit 176 - Ranged: 144~216 hit 176 - Magic: 144~216 hit 70
+- Level 50 - HP: 8 - MP: 4 - Defence: 114/129/112 - Dodge: 220 - Melee: 176~264 hit 216 - Ranged: 176~264 hit 216 - Magic: 176~264 hit 70
+- Level 52 - HP: 10 - MP: 4 - Defence: 118/134/116 - Dodge: 228 - Melee: 183~273 hit 224 - Ranged: 183~273 hit 224 - Magic: 183~273 hit 70
+- Level 60 - HP: 10 - MP: 4 - Defence: 135/153/132 - Dodge: 260 - Melee: 208~312 hit 256 - Ranged: 208~312 hit 256 - Magic: 208~312 hit 70
+- Level 62 - HP: 12 - MP: 4 - Defence: 139/158/136 - Dodge: 268 - Melee: 215~321 hit 264 - Ranged: 215~321 hit 264 - Magic: 215~321 hit 70
+- Level 70 - HP: 12 - MP: 4 - Defence: 156/177/153 - Dodge: 300 - Melee: 240~360 hit 296 - Ranged: 240~360 hit 296 - Magic: 240~360 hit 70
+- Level 72 - HP: 17 - MP: 4 - Defence: 160/181/157 - Dodge: 308 - Melee: 247~369 hit 304 - Ranged: 247~369 hit 304 - Magic: 247~369 hit 70
+- Level 80 - HP: 17 - MP: 4 - Defence: 176/200/173 - Dodge: 340 - Melee: 272~408 hit 336 - Ranged: 272~408 hit 336 - Magic: 272~408 hit 70
+- Level 82 - HP: 24 - MP: 4 - Defence: 180/205/177 - Dodge: 348 - Melee: 279~417 hit 344 - Ranged: 279~417 hit 344 - Magic: 279~417 hit 70
+- Level 90 - HP: 24 - MP: 4 - Defence: 197/224/193 - Dodge: 380 - Melee: 304~456 hit 376 - Ranged: 304~456 hit 376 - Magic: 304~456 hit 70
+- Level 99 - HP: 24 - MP: 4 - Defence: 216/245/212 - Dodge: 416 - Melee: 333~499 hit 412 - Ranged: 333~499 hit 412 - Magic: 333~499 hit 70
diff --git a/src/NosCore.Algorithm/MonsterStatService/IMonsterStatService.cs b/src/NosCore.Algorithm/MonsterStatService/IMonsterStatService.cs
new file mode 100644
index 0000000..f6d3477
--- /dev/null
+++ b/src/NosCore.Algorithm/MonsterStatService/IMonsterStatService.cs
@@ -0,0 +1,108 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+namespace NosCore.Algorithm.MonsterStatService
+{
+ ///
+ /// Service for calculating a monster's base statistics, before the flat modifiers of its monster entry
+ ///
+ public interface IMonsterStatService
+ {
+ ///
+ /// Gets the maximum HP of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The HP value
+ long GetMaxHp(byte race, byte level);
+
+ ///
+ /// Gets the maximum MP of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The MP value
+ long GetMaxMp(byte race, byte level);
+
+ ///
+ /// Gets the close combat defence of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's armour
+ /// The defence value
+ int GetCloseDefence(byte race, byte level, byte armorLevel);
+
+ ///
+ /// Gets the ranged defence of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's armour
+ /// The defence value
+ int GetDistanceDefence(byte race, byte level, byte armorLevel);
+
+ ///
+ /// Gets the magic defence of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's armour
+ /// The defence value
+ int GetMagicDefence(byte race, byte level, byte armorLevel);
+
+ ///
+ /// Gets the dodge of a monster, against close and ranged attacks alike
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's armour
+ /// The dodge value
+ int GetDodge(byte race, byte level, byte armorLevel);
+
+ ///
+ /// Gets the minimum damage of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's weapon
+ /// How the monster attacks
+ /// The damage value
+ int GetMinimumDamage(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass);
+
+ ///
+ /// Gets the maximum damage of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's weapon
+ /// How the monster attacks
+ /// The damage value
+ int GetMaximumDamage(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass);
+
+ ///
+ /// Gets the hit rate of a monster
+ ///
+ /// The race of the monster entry
+ /// The monster level
+ /// The level of the monster's weapon
+ /// How the monster attacks
+ /// The hit rate value
+ int GetHitRate(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass);
+
+ ///
+ /// Gets the critical hit chance of a monster, in percent
+ ///
+ /// The critical chance
+ int GetCriticalChance();
+
+ ///
+ /// Gets the extra damage of a monster's critical hit, in percent
+ ///
+ /// The critical damage rate
+ int GetCriticalRate();
+ }
+}
diff --git a/src/NosCore.Algorithm/MonsterStatService/MonsterAttackClass.cs b/src/NosCore.Algorithm/MonsterStatService/MonsterAttackClass.cs
new file mode 100644
index 0000000..6eb56cf
--- /dev/null
+++ b/src/NosCore.Algorithm/MonsterStatService/MonsterAttackClass.cs
@@ -0,0 +1,23 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+namespace NosCore.Algorithm.MonsterStatService
+{
+ ///
+ /// How a monster attacks, which decides its damage and hit rate curves
+ ///
+ public enum MonsterAttackClass : byte
+ {
+ /// Close combat
+ Melee = 0,
+
+ /// Ranged physical attacks
+ Ranged = 1,
+
+ /// Magic attacks, which have a flat hit rate
+ Magic = 2
+ }
+}
diff --git a/src/NosCore.Algorithm/MonsterStatService/MonsterStatService.cs b/src/NosCore.Algorithm/MonsterStatService/MonsterStatService.cs
new file mode 100644
index 0000000..31edbcc
--- /dev/null
+++ b/src/NosCore.Algorithm/MonsterStatService/MonsterStatService.cs
@@ -0,0 +1,184 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using NosCore.Algorithm.MateHpService;
+using NosCore.Algorithm.MateMpService;
+
+namespace NosCore.Algorithm.MonsterStatService
+{
+ ///
+ /// Calculates a monster's base statistics from its race, its level and the levels of its weapon and armour
+ ///
+ ///
+ /// Every curve is a race-specific share of the monster level, plus a fixed amount per equipment level,
+ /// plus the equipment's own upgrade bonus. HP and MP are the pet curves without a growth factor, and HP
+ /// is further multiplied in level tiers.
+ ///
+ public class MonsterStatService(IMateHpService mateHpService, IMateMpService mateMpService) : IMonsterStatService
+ {
+ private const int ArmorDefencePerLevel = 2;
+ private const int ArmorDodgePerLevel = 4;
+ private const int WeaponMinimumDamagePerLevel = 3;
+ private const int WeaponMaximumDamagePerLevel = 5;
+ private const int WeaponHitRatePerLevel = 4;
+ private const int MagicHitRate = 70;
+ private const int CriticalChance = 4;
+ private const int CriticalRate = 70;
+
+ private static readonly (byte Level, int Tenths)[] HpTiers =
+ [
+ (82, 35),
+ (72, 25),
+ (62, 18),
+ (52, 15),
+ (37, 12),
+ ];
+
+ private static readonly RaceCurves Default = new(
+ new(1, 2, 1, 15), new(1, 2, 0, 14), new(1, 2, 1, 10), new(1, 1, 0, 26),
+ new(1, 1, 0, 28), new(1, 1, 0, 34), new(1, 1, 0, 22),
+ new(1, 1, 0, 23), new(1, 1, 0, 29), 28,
+ new(1, 1, 0, 18), new(1, 1, 0, 24));
+
+ // Race 4 has two level 30 entries whose published stats fit none of these curves, so the low end
+ // of that race is unconfirmed.
+ private static readonly RaceCurves?[] Races =
+ [
+ Default,
+ new(new(1, 1, 0, 19), new(1, 1, 0, 17), new(1, 1, 0, 18), new(2, 1, 0, 33),
+ new(2, 1, 0, 35), new(2, 1, 0, 41), new(2, 1, 0, 29),
+ new(2, 1, 0, 30), new(2, 1, 0, 36), 42,
+ new(2, 1, 0, 33), new(2, 1, 0, 39)),
+ new(new(3, 4, 1, 14), new(1, 2, 1, 15), new(2, 5, 3, 14), new(1, 1, 0, 29),
+ new(3, 2, 1, 25), new(3, 2, 1, 31), new(1, 1, 0, 25),
+ new(1, 1, 0, 26), new(1, 1, 0, 32), 34,
+ new(4, 5, 1, 26), new(4, 5, 1, 32)),
+ new(new(1, 2, 1, 14), new(1, 2, 1, 15), new(1, 2, 1, 14), new(1, 1, 0, 29),
+ new(1, 1, 0, 26), new(1, 1, 0, 32), new(1, 1, 0, 25),
+ new(1, 1, 0, 26), new(1, 1, 0, 32), 34,
+ new(1, 1, 0, 26), new(1, 1, 0, 32)),
+ new(new(3, 5, 4, 16), new(3, 5, 4, 17), new(1, 1, 0, 16), new(6, 5, 4, 33),
+ new(6, 5, 4, 30), new(6, 5, 4, 36), new(6, 5, 4, 29),
+ new(6, 5, 4, 30), new(6, 5, 4, 36), 42,
+ new(2, 1, 0, 30), new(2, 1, 0, 36)),
+ new(new(2, 5, 0, 13), new(2, 5, 0, 14), new(2, 5, 0, 13), new(4, 5, 1, 26),
+ new(4, 5, 1, 23), new(4, 5, 1, 29), new(4, 5, 1, 22),
+ new(4, 5, 1, 23), new(4, 5, 1, 29), 28,
+ new(4, 5, 1, 23), new(4, 5, 1, 29)),
+ new(new(1, 2, 0, 11), new(1, 2, 1, 15), new(3, 4, 1, 24), new(1, 1, 0, 29),
+ new(1, 1, 0, 19), new(1, 1, 0, 25), new(1, 1, 0, 25),
+ new(1, 1, 0, 26), new(1, 1, 0, 32), 34,
+ new(3, 2, 1, 45), new(3, 2, 1, 51)),
+ null,
+ new(new(0, 1, 0, 10), new(0, 1, 0, 11), new(0, 1, 0, 10), new(0, 1, 0, 20),
+ new(0, 1, 0, 17), new(0, 1, 0, 23), new(0, 1, 0, 16),
+ new(0, 1, 0, 17), new(0, 1, 0, 23), 16,
+ new(0, 1, 0, 17), new(0, 1, 0, 23)),
+ ];
+
+ ///
+ public long GetMaxHp(byte race, byte level)
+ {
+ var tenths = 10;
+ foreach (var (tierLevel, tierTenths) in HpTiers)
+ {
+ if (level >= tierLevel)
+ {
+ tenths = tierTenths;
+ break;
+ }
+ }
+
+ return mateHpService.GetHp(race, level, 0) * tenths / 10;
+ }
+
+ ///
+ public long GetMaxMp(byte race, byte level) => mateMpService.GetMp(race, level, 0);
+
+ ///
+ public int GetCloseDefence(byte race, byte level, byte armorLevel) =>
+ For(race).Close.At(level) + (ArmorDefencePerLevel * armorLevel) + FloorDivide((2 * armorLevel) + 10, 25);
+
+ ///
+ public int GetDistanceDefence(byte race, byte level, byte armorLevel) =>
+ For(race).Distance.At(level) + (ArmorDefencePerLevel * armorLevel) + FloorDivide((9 * armorLevel) + 20, 25);
+
+ ///
+ public int GetMagicDefence(byte race, byte level, byte armorLevel) =>
+ For(race).Magic.At(level) + (ArmorDefencePerLevel * armorLevel) + FloorDivide(armorLevel + 5, 25);
+
+ ///
+ public int GetDodge(byte race, byte level, byte armorLevel) =>
+ For(race).Dodge.At(level) + (ArmorDodgePerLevel * armorLevel);
+
+ ///
+ public int GetMinimumDamage(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass)
+ {
+ var curves = For(race);
+ var curve = attackClass switch
+ {
+ MonsterAttackClass.Ranged => curves.RangedMinimum,
+ MonsterAttackClass.Magic => curves.MagicMinimum,
+ _ => curves.MeleeMinimum,
+ };
+ return curve.At(level) + (WeaponMinimumDamagePerLevel * weaponLevel) + WeaponUpgradeBonus(weaponLevel);
+ }
+
+ ///
+ public int GetMaximumDamage(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass)
+ {
+ var curves = For(race);
+ var curve = attackClass switch
+ {
+ MonsterAttackClass.Ranged => curves.RangedMaximum,
+ MonsterAttackClass.Magic => curves.MagicMaximum,
+ _ => curves.MeleeMaximum,
+ };
+ return curve.At(level) + (WeaponMaximumDamagePerLevel * weaponLevel) - WeaponUpgradeBonus(weaponLevel);
+ }
+
+ ///
+ public int GetHitRate(byte race, byte level, byte weaponLevel, MonsterAttackClass attackClass)
+ {
+ var curves = For(race);
+ return attackClass switch
+ {
+ MonsterAttackClass.Magic => MagicHitRate,
+ MonsterAttackClass.Ranged => (2 * curves.MeleeHitRate.LevelShare(level)) + (WeaponHitRatePerLevel * weaponLevel) + curves.RangedHitRateConstant,
+ _ => curves.MeleeHitRate.At(level) + (WeaponHitRatePerLevel * weaponLevel),
+ };
+ }
+
+ ///
+ public int GetCriticalChance() => CriticalChance;
+
+ ///
+ public int GetCriticalRate() => CriticalRate;
+
+ private static RaceCurves For(byte race) => race < Races.Length ? Races[race] ?? Default : Default;
+
+ private static int WeaponUpgradeBonus(byte weaponLevel) => FloorDivide(weaponLevel - 1, 5);
+
+ private static int FloorDivide(int value, int divisor) =>
+ value >= 0 ? value / divisor : -((-value + divisor - 1) / divisor);
+
+ ///
+ /// The race's share of the monster level, floor((numerator * level + offset) / denominator), plus a constant
+ ///
+ private readonly record struct Curve(int Numerator, int Denominator, int Offset, int Constant)
+ {
+ public int LevelShare(byte level) => FloorDivide((Numerator * level) + Offset, Denominator);
+
+ public int At(byte level) => LevelShare(level) + Constant;
+ }
+
+ private sealed record RaceCurves(
+ Curve Close, Curve Distance, Curve Magic, Curve Dodge,
+ Curve MeleeMinimum, Curve MeleeMaximum, Curve MeleeHitRate,
+ Curve RangedMinimum, Curve RangedMaximum, int RangedHitRateConstant,
+ Curve MagicMinimum, Curve MagicMaximum);
+ }
+}
diff --git a/test/NosCore.Algorithm.Tests/DocumentationTest.cs b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
index 777e3d4..b4e64ba 100644
--- a/test/NosCore.Algorithm.Tests/DocumentationTest.cs
+++ b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
@@ -791,5 +791,45 @@ public void MateHpMpDocumentation()
Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
}
+
+ [TestMethod]
+ public void MonsterStatDocumentation()
+ {
+ var service = new MonsterStatService.MonsterStatService(new MateHpService.MateHpService(), new MateMpService.MateMpService());
+
+ // Published stats with the entries' flat modifiers taken off: a race 0 level 55 monster, a race 2
+ // level 41 monster wearing level 44 armour, and a race 5 level 72 archer.
+ Assert.AreEqual(3919, service.GetMaxHp(0, 55));
+ Assert.AreEqual(969, service.GetMaxMp(0, 55));
+ Assert.AreEqual(157, service.GetCloseDefence(0, 55, 55));
+ Assert.AreEqual(171, service.GetDistanceDefence(0, 55, 55));
+ Assert.AreEqual(150, service.GetMagicDefence(0, 55, 55));
+ Assert.AreEqual(301, service.GetDodge(0, 55, 55));
+ Assert.AreEqual(258, service.GetMinimumDamage(0, 55, 55, MonsterStatService.MonsterAttackClass.Melee));
+ Assert.AreEqual(354, service.GetMaximumDamage(0, 55, 55, MonsterStatService.MonsterAttackClass.Melee));
+ Assert.AreEqual(297, service.GetHitRate(0, 55, 55, MonsterStatService.MonsterAttackClass.Melee));
+ Assert.AreEqual(3493, service.GetMaxHp(2, 41));
+ Assert.AreEqual(136, service.GetCloseDefence(2, 41, 44));
+ Assert.AreEqual(430, service.GetHitRate(5, 72, 72, MonsterStatService.MonsterAttackClass.Ranged));
+
+ var resultBuilder = new StringBuilder("# Monster Stat Table");
+ resultBuilder.AppendLine();
+ byte[] races = { 0, 1, 2, 3, 4, 5, 6, 8 };
+ byte[] levels = { 1, 10, 20, 30, 37, 40, 50, 52, 60, 62, 70, 72, 80, 82, 90, 99 };
+ foreach (var race in races)
+ {
+ resultBuilder.AppendLine();
+ resultBuilder.AppendLine($"## Race {race}");
+ foreach (var level in levels)
+ {
+ var classes = string.Join(" - ", Enum.GetValues().Select(attackClass =>
+ $"{attackClass}: {service.GetMinimumDamage(race, level, level, attackClass)}~{service.GetMaximumDamage(race, level, level, attackClass)} hit {service.GetHitRate(race, level, level, attackClass)}"));
+ resultBuilder.AppendLine(
+ $"- Level {level,2} - HP: {service.GetMaxHp(race, level)} - MP: {service.GetMaxMp(race, level)} - Defence: {service.GetCloseDefence(race, level, level)}/{service.GetDistanceDefence(race, level, level)}/{service.GetMagicDefence(race, level, level)} - Dodge: {service.GetDodge(race, level, level)} - {classes}");
+ }
+ }
+
+ Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
+ }
}
}