How to disable natural 1s and 20s?

Is there a way to mod, or hack, or something to disable the automatic fail/success of rolling a 1 and 20? I know you can disable 1s for just saving throws, but I want this to apply to everything.

this is built in to the game engine’s standard combat behaviour, and there is no config option, such as with saving throws. you could conceivably hook the attack event using nwnx or otherwise handle parts of the attack round manually. afaik there’s currently no nwnx function set that can change this aspect of combat, so you’d end up re-inventing the wheel. so unless this is very important to you and you’re willing to put in a lot of effort, i think the short answer to your question would be ‘no’.

Ah, too bad.

This wouldn’t be too hard to do with nwnx. nwnx2 (for 1.69) development is mostly dead, though, so if you lack the skills to do it yourself, you’ll need to motivate someone, likely from the nwnx:ee group. If anyone wants this for EE, they can just file a request at github.com/nwnxee/unified/issues

While is it already possible disable the 1 (auto fail) through nwnplayer.ini
isn’t possible doing so with 20.

#Sherincall
I would really like a function that would allow to disable the automatic success (20).
A friend of mine already proposed this during a livestream, Trent Oster replied that it was possible to do so but did not say whether such work would be done in the future. So I think the only possibility is to do it through nwnx.

Beamdog simply does not have enough resources to implement and test all these requests. There has been a lot of “un-hardcoding” going on lately, but whether any particular thing will be opened or not depends on low level state of the code that implements it.

Here’s an example of things that have recently been opened up to customization:
https://gist.githubusercontent.com/Daztek/7e30bbe841e3c80ac1e416eacd2b3d07/raw/f183ea056a3c9dad1837c6afd581ccf3668b8171/ruleset.2da

Any more niche features that require such deep level changes are exactly why nwnx exists. Unfortunately for some, nwnx only works for dedicated servers, so it is not really usable in singleplayer

1 Like

thx
Of course this is not a request from a person that is planning to create a SP module, quite the contrary it is needed to improve my old pw.

Oh, I wanted this to be used in singleplayer modules.
Also, the one at nwnplayer.ini only affects saving throws, doesn’t work for other stuff.

I checked out the NWNX thing but it seemed fairly confusing, not sure if I can do it or even know where to start.

Maybe if you can tell us the “why” of your request, we can figure out other options.

I just don’t like getting hit with 50+ AC against mobs that only have 20 attack chance.

What’t the point of fighting them then? There always needs to be a way for things to be at least a little bit dangerous. Seems boring.

1 Like

If you have to ask that question, then whatever I say won’t satisfy you.

1 Like

It could be very useful in a pvp server where critical failure (1) is already turned off,so in order to rebalance the game the admin may also need to disable the automatic success (20) .

Alright guys, so I worked it out, made a “mod” that removes natural 20s, for everyone. You have to use cheat engine, so just copy the below into a .txt file, rename it to a .ct file and open it with cheat engine. There should be two boxes at the bottom, tick the one that says “no natural 20s”, not the other one. This will only work on the diamond edition, I have no idea if it will work in the enhanced one, probably not my guess.

Also, keep in mind, this works by finding out if the roll was a 20, then changing it to a 19 before any checks are done to it. So if you have a weapon that will only crit on 20s, then sorry, you can’t crit anymore, though the chance to crit won’t be affected if it’s 19-20, sorry, couldn’t be asked to fix this part.

I have no idea if this will work in multiplayer, it may use a different function for that, not sure. Keep in mind everyone involved in the server should have it on, otherwise some unpredictable stuff will happen.

<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="26">
  <CheatEntries>
    <CheatEntry>
      <ID>0</ID>
      <Description>"old, don't touch"</Description>
      <LastState/>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)

newmem: //this is allocated memory, you have read,write,execute access
    pop edx
    pop ebx
    pop eax
mov eax,#18
jmp exit

originalcode:
and eax,00007FFF

    push eax
    push ebx
    push edx
mov ebx,#20
div ebx
cmp edx,#19
je newmem
    pop edx
    pop ebx
    pop eax

exit:
jmp returnhere

"nwmain.exe"+44C118:
jmp originalcode
returnhere:


[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"nwmain.exe"+44C118:
and eax,00007FFF
//Alt: db 25 FF 7F 00 00
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>1</ID>
      <Description>"no natural 20s"</Description>
      <LastState Activated="1"/>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>{ Game   : nwmain.exe
  Version:
  Date   : 2019-09-10
  Author : deama

  This script does blah blah blah
}

[ENABLE]

aobscanmodule(INJECT,nwmain.exe,25 FF 7F 00 00 C3) // should be unique
alloc(newmem,$1000)

label(code)
label(return)

newmem:
    pop edx
    pop ebx
    pop eax
mov eax,#18
jmp return


code:
and eax,00007FFF
///////////////////////////
    push eax
    push ebx
    push edx
mov ebx,#20
div ebx
cmp edx,#19
je newmem
    pop edx
    pop ebx
    pop eax
///////////////////////////
jmp return

INJECT:
  jmp code
return:
registersymbol(INJECT)

[DISABLE]

INJECT:
  db 25 FF 7F 00 00

unregistersymbol(INJECT)
dealloc(newmem)

{
// ORIGINAL CODE - INJECTION POINT: "nwmain.exe"+44C118

"nwmain.exe"+44C0F4: 8B 4C 24 04           -  mov ecx,[esp+04]
"nwmain.exe"+44C0F8: 89 48 14              -  mov [eax+14],ecx
"nwmain.exe"+44C0FB: C3                    -  ret
"nwmain.exe"+44C0FC: E8 B2 3C 00 00        -  call nwmain.exe+44FDB3
"nwmain.exe"+44C101: 8B 48 14              -  mov ecx,[eax+14]
"nwmain.exe"+44C104: 69 C9 FD 43 03 00     -  imul ecx,ecx,000343FD
"nwmain.exe"+44C10A: 81 C1 C3 9E 26 00     -  add ecx,00269EC3
"nwmain.exe"+44C110: 89 48 14              -  mov [eax+14],ecx
"nwmain.exe"+44C113: 8B C1                 -  mov eax,ecx
"nwmain.exe"+44C115: C1 E8 10              -  shr eax,10
// ---------- INJECTING HERE ----------
"nwmain.exe"+44C118: 25 FF 7F 00 00        -  and eax,00007FFF
// ---------- DONE INJECTING  ----------
"nwmain.exe"+44C11D: C3                    -  ret
"nwmain.exe"+44C11E: CC                    -  int 3
"nwmain.exe"+44C11F: CC                    -  int 3
"nwmain.exe"+44C120: 83 EC 08              -  sub esp,08
"nwmain.exe"+44C123: DD 14 24              -  fst qword ptr [esp]
"nwmain.exe"+44C126: E8 1D 71 00 00        -  call nwmain.exe+453248
"nwmain.exe"+44C12B: E8 0D 00 00 00        -  call nwmain.exe+44C13D
"nwmain.exe"+44C130: 83 C4 08              -  add esp,08
"nwmain.exe"+44C133: C3                    -  ret
"nwmain.exe"+44C134: 8D 54 24 04           -  lea edx,[esp+04]
}
</AssemblerScript>
    </CheatEntry>
  </CheatEntries>
  <CheatCodes>
    <CodeEntry>
      <Description>Change of test ebp,ebp</Description>
      <Address>0064A295</Address>
      <ModuleName>nwmain.exe</ModuleName>
      <ModuleNameOffset>24A295</ModuleNameOffset>
      <Before>
        <Byte>6C</Byte>
        <Byte>24</Byte>
        <Byte>40</Byte>
        <Byte>75</Byte>
        <Byte>3D</Byte>
      </Before>
      <Actual>
        <Byte>85</Byte>
        <Byte>ED</Byte>
      </Actual>
      <After>
        <Byte>74</Byte>
        <Byte>39</Byte>
        <Byte>66</Byte>
        <Byte>8B</Byte>
        <Byte>45</Byte>
      </After>
    </CodeEntry>
    <CodeEntry>
      <Description>Change of jne nwmain.exe+24A2B9</Description>
      <Address>0064A2AC</Address>
      <ModuleName>nwmain.exe</ModuleName>
      <ModuleNameOffset>24A2AC</ModuleNameOffset>
      <Before>
        <Byte>FF</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
        <Byte>3B</Byte>
        <Byte>C1</Byte>
      </Before>
      <Actual>
        <Byte>75</Byte>
        <Byte>0B</Byte>
      </Actual>
      <After>
        <Byte>BF</Byte>
        <Byte>01</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
      </After>
    </CodeEntry>
    <CodeEntry>
      <Description>Change of call dword ptr [edx+7C]</Description>
      <Address>006BF170</Address>
      <ModuleName>nwmain.exe</ModuleName>
      <ModuleNameOffset>2BF170</ModuleNameOffset>
      <Before>
        <Byte>56</Byte>
        <Byte>55</Byte>
        <Byte>57</Byte>
        <Byte>8B</Byte>
        <Byte>C8</Byte>
      </Before>
      <Actual>
        <Byte>FF</Byte>
        <Byte>52</Byte>
        <Byte>7C</Byte>
      </Actual>
      <After>
        <Byte>E9</Byte>
        <Byte>CB</Byte>
        <Byte>FE</Byte>
        <Byte>FF</Byte>
        <Byte>FF</Byte>
      </After>
    </CodeEntry>
    <CodeEntry>
      <Description>Change of mov eax,[esp+10]</Description>
      <Address>7754DEC0</Address>
      <ModuleName>ntdll.dll</ModuleName>
      <ModuleNameOffset>2DEC0</ModuleNameOffset>
      <Before>
        <Byte>35</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
      </Before>
      <Actual>
        <Byte>8B</Byte>
        <Byte>44</Byte>
        <Byte>24</Byte>
        <Byte>10</Byte>
      </Actual>
      <After>
        <Byte>89</Byte>
        <Byte>6C</Byte>
        <Byte>24</Byte>
        <Byte>10</Byte>
        <Byte>8D</Byte>
      </After>
    </CodeEntry>
    <CodeEntry>
      <Description>Change of call nwmain.exe+44FDB3</Description>
      <Address>0084C0FC</Address>
      <ModuleName>nwmain.exe</ModuleName>
      <ModuleNameOffset>44C0FC</ModuleNameOffset>
      <Before>
        <Byte>04</Byte>
        <Byte>89</Byte>
        <Byte>48</Byte>
        <Byte>14</Byte>
        <Byte>C3</Byte>
      </Before>
      <Actual>
        <Byte>E8</Byte>
        <Byte>B2</Byte>
        <Byte>3C</Byte>
        <Byte>00</Byte>
        <Byte>00</Byte>
      </Actual>
      <After>
        <Byte>8B</Byte>
        <Byte>48</Byte>
        <Byte>14</Byte>
        <Byte>69</Byte>
        <Byte>C9</Byte>
      </After>
    </CodeEntry>
  </CheatCodes>
  <UserdefinedSymbols/>
  <Structures StructVersion="2">
    <Structure Name="unnamed structure" AutoFill="0" AutoCreate="1" DefaultHex="0" AutoDestroy="0" DoNotSaveLocal="0" RLECompression="1" AutoCreateStructsize="4096">
      <Elements>
        <Element Offset="0" Vartype="4 Bytes" Bytesize="4" RLECount="404" DisplayMethod="Unsigned Integer"/>
      </Elements>
    </Structure>
  </Structures>
  <Comments>Info about this table:
</Comments>
</CheatTable>

Sorry for being off-topic, but I wished somebody determined enough would find a way (either via in-memory patch or by finding the right bytes in the binary) to make <StartHighlight> change text color to yellow instead of that damn blue. Yes, I know it can be made green or red through stringtokens.2da, but that doesn’t help much.