Apply 4.1ish monster to-hit calculations. M_FIGHTER monsters get bonus to-hit.
Split melee and beam to-hit calculations again. Melee to-hit is now largely 4.1ish, but uses randomised player evasion.
Applied 4.1 unique rebalancing. Higher level uniques now hit harder and have more hp.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1074 c06c8d41-db1a-0410-9941-cceddc491573
FUEEIUKGHHFPIRZCN3N753GONWAZTWQ2ZWR53IBJAAZ6FZUNGOMAC ZHFUXYUHS6V47WK2NRH7OU6RX77NRKTXOZC3MND2GG7PEEWSGFTAC GS2I6EO4OQZ5FHQJQ6YRUDPSJ5XWGLHN544MVQPSF2PATUSJFSSAC IIDDQROLZFP47VSCQUUCI4A5VGV762RJBTT3GM2K4AFRE5KZ4BFAC EWYQBSLHJPWW2HWOPSPK7BYJ4Q62IYCM5SI4YK6QQS6TTDWOM7BQC 7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC NW74NALMBEWIKEOBIXA65RQULHS6M4GZ4S5IWDMEGWUAAAY7CQNQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC RIMJO42HB75SAZH44KZ2UH2QULOPIJLMEN4CR2CYNR2EKEKLFHSQC { {AT_HIT, AF_PLAIN, 3}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 1, 0, 0, 14 },
{ {AT_HIT, AF_PLAIN, 5}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 1, 0, 0, 20 },
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 10, 0, 0, 53 },
{ {AT_HIT, AF_PLAIN, 25}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 10, 0, 0, 105 },
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 11, 0, 0, 60 },
{ {AT_HIT, AF_PLAIN, 18}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 11, 0, 0, 90 },
{ {AT_HIT, AF_PLAIN, 11}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 11, 0, 0, 64 },
{ {AT_HIT, AF_PLAIN, 25}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 11, 0, 0, 140 },
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 13, 0, 0, 55 },
{ {AT_HIT, AF_PLAIN, 24}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 13, 0, 0, 118 },
{ {AT_HIT, AF_PLAIN, 12}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 13, 0, 0, 52 },
{ {AT_HIT, AF_PLAIN, 17}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 13, 0, 0, 106 },
{ {AT_HIT, AF_PLAIN, 12}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 14, 0, 0, 67 },
{ {AT_HIT, AF_PLAIN, 19}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 14, 0, 0, 110 },
{ {AT_HIT, AF_PLAIN, 11}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 14, 0, 0, 70 },
{ {AT_HIT, AF_PLAIN, 29}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 14, 0, 0, 121 },
{ {AT_HIT, AF_PLAIN, 13}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 16, 0, 0, 80 },
{ {AT_HIT, AF_PLAIN, 21}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 16, 0, 0, 123 },
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 17, 0, 0, 78 },
{ {AT_HIT, AF_PLAIN, 22}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 17, 0, 0, 140 },
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 18, 0, 0, 83 },
{ {AT_HIT, AF_PLAIN, 22}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 18, 0, 0, 136 },
{ {AT_HIT, AF_PLAIN, 16}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 20, 0, 0, 95 },
{ {AT_HIT, AF_PLAIN, 36}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 20, 0, 0, 214 },
{ {AT_HIT, AF_PLAIN, 17}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 21, 0, 0, 105 },
{ {AT_HIT, AF_PLAIN, 27}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 21, 0, 0, 159 },
{ {AT_HIT, AF_PLAIN, 18}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 22, 0, 0, 119 },
{ {AT_HIT, AF_PLAIN, 30}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 22, 0, 0, 164 },
{ {AT_HIT, AF_PLAIN, 15}, {AT_TOUCH, AF_DRAIN_XP, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 22, 0, 0, 99 },
{ {AT_HIT, AF_PLAIN, 25}, {AT_TOUCH, AF_DRAIN_XP, 15}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },{ 22, 0, 0, 154 },
bool test_melee_hit(int to_hit, int ev){int roll = -1;int margin = AUTOMATIC_HIT;ev *= 2;if (to_hit >= AUTOMATIC_HIT)return (true);else if (random2(1000) < 10 * MIN_HIT_MISS_PERCENTAGE)margin = (coinflip() ? 1 : -1) * AUTOMATIC_HIT;else{roll = random2( to_hit + 1 );margin = (roll - random2avg(ev, 2));}#if DEBUG_DIAGNOSTICSfloat miss;if (to_hit < ev)miss = 100.0 - static_cast<float>( MIN_HIT_MISS_PERCENTAGE ) / 2.0;else{miss = static_cast<float>( MIN_HIT_MISS_PERCENTAGE ) / 2.0+ static_cast<float>( (100 - MIN_HIT_MISS_PERCENTAGE) * ev )/ static_cast<float>( to_hit );}mprf( MSGCH_DIAGNOSTICS,"to hit: %d; ev: %d; miss: %0.2f%%; roll: %d; result: %s%s (%d)",to_hit, ev, miss, roll, (margin >= 0) ? "hit" : "miss",(roll == -1) ? "!!!" : "", margin );#endifreturn (margin >= 0);}
int flags = MB_YESNO + // want abort and ignore buttons// (too bad we can't ditch the retry button...)MB_ICONERROR + // display the icon for errorsMB_TASKMODAL + // don't let the user do anything else in the appMB_SETFOREGROUND; // bring the app to the frontstrcpy(text, mesg);strcat(text, "\nDo you want to drop into the debugger?");int result = MessageBoxA(NULL, text, "Debug Break", flags);if (result == IDYES)MyDebugBreak();if (quitting)PostQuitMessage(msg.wParam);
#endif}#endif//---------------------------------------------------------------//// IsDebuggerPresent95//// From March 1999 Windows Developer's Journal. This should only// be called if we're running on Win 95 (normally I'd add an// ASSERT, but that's a bit dicy since this is called by ASSERT...)////---------------------------------------------------------------#if WINstatic bool IsDebuggerPresent95(){bool present = false;const DWORD kDebuggerPresentFlag = 0x000000001;const DWORD kProcessDatabaseBytes = 190;const DWORD kOffsetFlags = 8;DWORD threadID = GetCurrentThreadId();DWORD processID = GetCurrentProcessId();DWORD obfuscator = 0;#if __MWERKS__asm{mov ax, fsmov es, axmov eax, 0x18mov eax, es:[eax]sub eax, 0x10 xor eax,[threadID] mov[obfuscator], eax}#else_asm{mov ax, fsmov es, axmov eax, 18 hmov eax, es:[eax]sub eax, 10 h xor eax,[threadID] mov[obfuscator], eax}#endifconst DWORD *processDatabase =reinterpret_cast< const DWORD * >(processID ^ obfuscator);if (!IsBadReadPtr(processDatabase, kProcessDatabaseBytes)){DWORD flags = processDatabase[kOffsetFlags];present = (flags & kDebuggerPresentFlag) != 0;}return present;}#endif//---------------------------------------------------------------//// IsDebuggerPresent////---------------------------------------------------------------#if WINbool IsDebuggerPresent(){bool present = false;typedef BOOL(WINAPI * IsDebuggerPresentProc) ();HINSTANCE kernelH = LoadLibrary("KERNEL32.DLL");if (kernelH != NULL){ // should never failIsDebuggerPresentProc proc =(IsDebuggerPresentProc)::GetProcAddress( kernelH,"IsDebuggerPresent" );if (proc != NULL) // only present in NT and Win 98present = proc() != 0;elsepresent = IsDebuggerPresent95();}return present;}#endif//---------------------------------------------------------------//// CreateConsoleWindow////---------------------------------------------------------------#if WINstatic void CreateConsoleWindow(){ASSERT(sConsole == NULL);// Create the console windowif (::AllocConsole()){// Get the console window's handlesConsole =::GetStdHandle(STD_ERROR_HANDLE);if (sConsole == INVALID_HANDLE_VALUE)sConsole = NULL;// Set some optionsif (sConsole != NULL){VERIFY(::SetConsoleTextAttribute(sConsole, FOREGROUND_GREEN));// green text on a black background (there doesn't appear to// be a way to get black text)VERIFY(::SetConsoleTitle("Debug Log"));COORD size = { 80, 120 };VERIFY(::SetConsoleScreenBufferSize(sConsole, size));}elseDEBUGSTR(L "Couldn't get the console window's handle!");}elseDEBUGSTR(L "Couldn't allocate the console window!");}#endif#if DEBUG//---------------------------------------------------------------//// TraceString////---------------------------------------------------------------static void TraceString(const char *mesg){// Write the string to the debug window#if WINif (IsDebuggerPresent()){OutputDebugStringA(mesg); // if you're using CodeWarrior you'll need to enable the "Log System Messages" checkbox to get this working}else{if (sConsole == NULL) // otherwise we'll use a console windowCreateConsoleWindow();if (sConsole != NULL){unsigned long written;VERIFY(WriteConsoleA(sConsole, mesg, strlen(mesg), &written, NULL));}}#elsefprintf(stderr, "%s", mesg);
// Write the string to the debug logstatic bool inited = false;static FILE *file = NULL;if (!inited){ASSERT(file == NULL);const char *fileName = "DebugLog.txt";file = fopen(fileName, "w");ASSERT(file != NULL);inited = true;}if (file != NULL){fputs(mesg, file);fflush(file); // make sure all the output makes it to the file}
//---------------------------------------------------------------//// TRACE////---------------------------------------------------------------#if DEBUGvoid TRACE(const char *format, ...){char mesg[2048];va_list args;va_start(args, format);vsprintf(mesg, format, args);va_end(args);TraceString(mesg);}#endif // DEBUG
bool melee,const item_def *weap,int wskill, unsigned long damage,long iterations, long hits,int maxdam, unsigned long time)
bool melee,const item_def *weap,const char *wskill,unsigned long damage,long iterations, long hits,int maxdam, unsigned long time)
static void fsim_defence_item(FILE *out, long cum, int hits, int max,int speed, long iters){// AC | EV | Arm | Dod | Acc | Av.Dam | Av.HitDam | Eff.Dam | Max.Dam | Av.Timefprintf(out, "%2d %2d %2d %2d %3ld%% %5.2f %5.2f %5.2f %3d"" %2d\n",player_AC(),player_evasion(),you.skills[SK_DODGING],you.skills[SK_ARMOUR],100 * hits / iters,double(cum) / iters,hits? double(cum) / hits : 0.0,double(cum) / iters * speed / 10,max,100 / speed);}
fsim_item(out, false, item, wskill, cumulative_damage,iter_limit, hits, maxdam, time_taken);
fsim_item(out, false, item, make_stringf("%2d", wskill).c_str(),cumulative_damage, iter_limit, hits, maxdam, time_taken);return (true);}static bool fsim_mon_melee(FILE *out, int dodge, int armour, int mi){you.skills[SK_DODGING] = dodge;you.skills[SK_ARMOUR] = armour;const int yhp = you.hp;const int ymhp = you.hp_max;unsigned long cumulative_damage = 0L;long hits = 0L;int maxdam = 0;no_messages mx;for (long i = 0; i < Options.fsim_rounds; ++i){you.hp = you.hp_max = 5000;monster_attack(mi);const int damage = you.hp_max - you.hp;if (damage)hits++;cumulative_damage += damage;if (damage > maxdam)maxdam = damage;}you.hp = yhp;you.hp_max = ymhp;
static void fsim_defence_title(FILE *o, int mon){fprintf(o, CRAWL " version " VERSION "\n\n");fprintf(o, "Combat simulation: %s vs. %s %s (%ld rounds) (%s)\n",menv[mon].name(DESC_PLAIN).c_str(),species_name(you.species, you.experience_level),you.class_name,Options.fsim_rounds,fsim_time_string().c_str());fprintf(o, "Experience: %d\n", you.experience_level);fprintf(o, "Strength : %d\n", you.strength);fprintf(o, "Intel. : %d\n", you.intel);fprintf(o, "Dexterity : %d\n", you.dex);fprintf(o, "Base speed: %d\n", player_speed());fprintf(o, "\n");fsim_mon_stats(o, menv[mon]);fprintf(o, "\n");fprintf(o, "AC | EV | Dod | Arm | Acc | Av.Dam | Av.HitDam | Eff.Dam | Max.Dam | Av.Time\n");}
static bool debug_fight_sim(int mindex, int missile_slot)
static bool fsim_mon_hit_you(FILE *ostat, int mindex, int){fsim_defence_title(ostat, mindex);for (int sk = 0; sk <= 27; ++sk){mesclr();mprf("Calculating average damage for %s at dodging %d",menv[mindex].name(DESC_PLAIN).c_str(),sk);if (!fsim_mon_melee(ostat, sk, 0, mindex))return (false);fflush(ostat);// Not checking in the combat loop itself; that would be more responsive// for the user, but slow down the sim with all the calls to kbhit().if (kbhit() && getch() == 27){mprf("Canceling simulation\n");return (false);}}for (int sk = 0; sk <= 27; ++sk){mesclr();mprf("Calculating average damage for %s at armour %d",menv[mindex].name(DESC_PLAIN).c_str(),sk);if (!fsim_mon_melee(ostat, 0, sk, mindex))return (false);fflush(ostat);// Not checking in the combat loop itself; that would be more responsive// for the user, but slow down the sim with all the calls to kbhit().if (kbhit() && getch() == 27){mprf("Canceling simulation\n");return (false);}}mprf("Done defence simulation with %s",menv[mindex].name(DESC_PLAIN).c_str());return (true);}static bool fsim_you_hit_mon(FILE *ostat, int mindex, int missile_slot){fsim_title(ostat, mindex, missile_slot);for (int wskill = 0; wskill <= 27; ++wskill){mesclr();mprf("Calculating average damage for %s at skill %d",fsim_weapon(missile_slot).c_str(), wskill);if (!debug_fight_simulate(ostat, wskill, mindex, missile_slot))return (false);fflush(ostat);// Not checking in the combat loop itself; that would be more responsive// for the user, but slow down the sim with all the calls to kbhit().if (kbhit() && getch() == 27){mprf("Canceling simulation\n");return (false);}}mprf("Done fight simulation with %s", fsim_weapon(missile_slot).c_str());return (true);}static bool debug_fight_sim(int mindex, int missile_slot,bool (*combat)(FILE *, int mind, int mslot))
fsim_title(ostat, mindex, missile_slot);for (int wskill = 0; wskill <= 27; ++wskill){mesclr();mprf("Calculating average damage for %s at skill %d",fsim_weapon(missile_slot).c_str(), wskill);if (!debug_fight_simulate(ostat, wskill, mindex, missile_slot))goto done_combat_sim;fflush(ostat);// Not checking in the combat loop itself; that would be more responsive// for the user, but slow down the sim with all the calls to kbhit().if (kbhit() && getch() == 27){success = false;mprf("Canceling simulation\n");goto done_combat_sim;}}
combat(ostat, mindex, missile_slot);
debug_fight_sim(mindex, -1);goto fsim_mcleanup;}for (int i = 0, size = Options.fsim_kit.size(); i < size; ++i){int missile = fsim_kit_equip(Options.fsim_kit[i]);if (missile == -100)
for (int i = 0, size = Options.fsim_kit.size(); i < size; ++i)
mprf("Aborting sim on %s", Options.fsim_kit[i].c_str());goto fsim_mcleanup;
int missile = fsim_kit_equip(Options.fsim_kit[i]);if (missile == -100){mprf("Aborting sim on %s", Options.fsim_kit[i].c_str());break;}if (!debug_fight_sim(mindex, missile, fsim_you_hit_mon))break;