Illarion's NeoStasis script v1.4

illarion

Member
# NeoStasis
# v1.4
# by Illarion, Mar 2007
# A KoL Stasis script for the post-noodle world.
# formulae courtesy of HCO forums
# Many thanks to Holatuwol and Veracity, without whom this never would have been possible
# Thanks to dirkdiggler for leatherback formula

# Change history
# v1.4 - better cocoon support, better handling of situations when mob or player cannot hit,
# fixed panic noodling, included Gemelli's workaround for undefined monsters
# v1.3 - fixed entangling noodle bug?, FinishNOW implemented, aware of physical-resistant monsters,
# sanity check when monster can't hit player.
# v1.2- fixed shield/hero test bug, tidied display messages, used antidote
# v1.1 - Location checking for dictionary, pickpocket, fixed mp_cost check
# v1.0 - Forked from ASHStasis v1.4

Here it is guys and gals, my latest stasis script attempt. Sadly, I was a lot happier with it before I trashed my hard drive and had to rewrite it, but isn't that always the way? :)
As always, I'd greatly appreciate comments, criticisms and advice, especially on the new, post-noodle combat logic:

Current rules are, in order of precedence:

If round = 28+, try to kill enemy (TS, LTS, Stream)
WIP - If we expect the enemy to kill us next hit:
- cast noodles if we have it (and haven't already), otherwise:
- heal up to safety level while the enemy remains noodled, or kill as quickly as possible when noodles break
Heal up to safety level
Do nothing

No handling for delevellers at all yet - I'll wait until it's been spaded out whether Boogaloo/Shieldbutt are worth it.

Given the complete rewrite, and the noodle change, it hardly needs saying that this is way back into beta - please don't complain to me if it messes up your leaderboard attempt! ;)
Testing feedback would be most helpful though - just save it for your prep runs at first.

Code:
#  NeoStasis.ash
#  v1.4
#  by Illarion, Mar 2007
#  A KoL Stasis script for the post-noodle world.
#  formulae courtesy of HCO forums
#  Many thanks to Holatuwol and Veracity, without whom this never would have been possible
#  Thanks to dirkdiggler for leatherback formula

#  Change history
#  v1.4 - better cocoon support, better handling of situations when mob or player cannot hit,
#	fixed panic noodling, included Gemelli's workaround for undefined monsters
#  v1.3 - fixed entangling noodle bug?, FinishNOW implemented, aware of physical-resistant monsters,
#	sanity check when monster can't hit player.
#  v1.2- fixed shield/hero test bug, tidied display messages, used antidote
#  v1.1 - Location checking for dictionary, pickpocket, fixed mp_cost check
#  v1.0 - Forked from ASHStasis v1.4

#TODO
#Monster variance
#abort on specific monsters

#*** Begin user-configurable variables ***#

//Change to false if antidote is changed!
boolean USE_ANTIDOTE = true;
//boolean USE_ANTIDOTE = false;

#Default values:
int DAM_THRESHOLD = 20;

//Script will Salve to keep health above this percentage.
//Set low to use with Coccoon, eg 20, or high without, eg 80-90.
int HEALTH_PCT = 60;
if (have_skill($skill[Cannelloni Cocoon]) && my_maxhp()>100)
{
	HEALTH_PCT = 20;
}

//if your moxie exceeds monster attack by this much, don't bother trying to stasis (as mob can't hit)
int OVERMOXIE_LEVEL = 8;

#print debug messages.  These go to the Mafia status bar, and the gCLI.  Turn them off if
#you're happy with the performance, and don't care about tweaking/understanding what's going on.
boolean dbug = true;

//Use to show more detailed output
boolean bVerbose = true;

#*** End of user-configurable variables ***#

#Used for status messages
int giRound;

int giCurDelevel;
boolean gbEnemyNoodled;
boolean gbInValley;
stat gDefenceStat;
monster zilch=$monster[];

string MSG_NOODLEBREAK = "Your opponent struggles free of your entangling noodles.";

int max(int a, int b)
{
	int iRetVal;
 	if (a > b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int min(int a, int b)
{
	int iRetVal;
 	if (a < b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int LeatherBackDR()
{
	//DR from Leatherback, as dirkdiggler's formula:
	//Leatherback DR = Clamp[ 1 < ceil( (level-3)/2 ) ]

	int iResult;
	if (have_skill($skill[skin of the leatherback]))
	{
		iResult = (max(1, ceil( (my_level()-3)/2 )));
	}
	else
	{
		iResult = 0;
	}
	return iResult;
}

void ShowStatusMsg(string Msg)
{
	print( "Round: " + giRound + " " +
	"(MP Change=" + (my_mp()-string_to_int(get_property("IllStasis_StartMP"))) +      				", HP Change=" + (my_hp()-string_to_int(get_property("IllStasis_StartHP"))) +  ") --> " + Msg );
}

boolean ShieldEquipped()
{
	boolean bFoundShield = false;
	string sItem;
	sItem = item_to_string(current_equipment($slot[off-hand]));
	bFoundShield = bFoundShield || (index_of(sItem, "buckler" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "shield" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "hors d'oeuvre tray" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "box turtle" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "coffin lid" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "sewer turtle" )>=0);

	if (bVerbose) { Print("Offhand=" + sItem + "-> Shield=" + boolean_to_string(bFoundShield)); }

	return bFoundShield;
}

int CheckSkill(skill myskill)
//return 0=success, 1=insufficient mana, 2=do not have skill
{
    if (!have_skill(myskill))
    {
       return 2;
    }
    else
    {
		if (my_mp() < mp_cost(myskill) )
		{
		    return 1;
		}
	}
	return 0;
}

boolean CheckPhysicalResistance(monster eek)
{
	if (eek == $monster[chalkdust wraith])
		return true;
	if (eek == $monster[Ghost Miner])
		return true;
	if (eek == $monster[Snow Queen])
		return true;
	return false;
}

int ExpectedMonsterDamage(monster eek)
//BaseMonsterDmg = (20% to 25%)*Atk + (Atk-Mox)
//Dam = (Base - DR) * DA * ER
{
 	  int iDam;
 	  int iAtk;
 	  int iMoxPenalty;
 	  int iDR;
	  iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;

	  iMoxPenalty = max(iAtk - my_buffedstat(gDefenceStat),0);
	  iDam = 0.25*iAtk + iMoxPenalty;
	  iDam = iDam - damage_reduction()-LeatherbackDR();
	  iDam = iDam - (iDam * damage_absorption_percent()/100);

	  //TODO elemental resistance

	  if (bVerbose) { Print("Expected:  (25% of " + iAtk + "+" + iMoxPenalty + "-(" + damage_reduction() + "+" + LeatherBackDR() + ")DR) * " + damage_absorption_percent() + "%DA = " + iDam); }

	  if(eek==zilch)
	  {
		if (dbug) { Print("Monster undefined ... assuming a reasonable range."); }
		iDam=10;
	  }

	  return iDam;
}

boolean MonsterCannotHit(monster eek)
//Test if mob can hit us
{
	int iAtk;
	iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;
	return ((iAtk + OVERMOXIE_LEVEL) <= my_buffedstat($stat[moxie]) );
}

boolean PlayerCannotHit(monster eek)
//Test if we can hit the mob
{
	int iDef;
	iDef = monster_base_defense(eek)+ monster_level_adjustment() - giCurDelevel;
	return (my_buffedstat(current_hit_stat()) < (iDef + 7)); //TODO check the 7
}

void PickPocket()
{
	print("Picking pocket...");
	visit_url("fight.php?action=steal");

}

boolean ThrowJunkItem()
{
	item JunkItem;
	JunkItem = $item[scroll of turtle summoning];
	if (bVerbose) { Print("Trying to throw junk..."); }

	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[seal tooth];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[spices];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}

	if (bVerbose) { Print("Failed to throw junk."); }
	return false;
}



void FinishHim(monster eek)
{
	if (dbug) { print("Finish Him!"); }
	if (gbInValley)
	//Best to use a dictionary if we can - free
	{
		if (item_amount($item[dictionary])>0)
		{
			if (bVerbose) { Print("Finish with dictionary"); }
			throw_item($item[dictionary]);
		}
		else
		{
			if (item_amount($item[facsimile dictionary])>0)
			{
				if (bVerbose) { Print("Finish with dictionary"); }
				throw_item($item[facsimile dictionary]);
			}
		}
	}
	else
	{
		if (CheckPhysicalResistance(eek) && have_skill($skill[immaculate seasoning]) && 		CheckSkill($skill[stream of sauce])==0 )
		{
			if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
			use_skill($skill[stream of sauce]);
		}
		else
		{
			#check for melee weapon
			if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0)
			{
				if (bVerbose) { Print("Finish with TS"); }
				use_skill($skill[thrust-smack]);
			}
			else
			{
				if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
				{
					if (bVerbose) { Print("Finish with Stream"); }
					use_skill($skill[stream of sauce]);
				}
				else
				{
					if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0)
					{
						if (bVerbose) { Print("Finish with LTS"); }
						use_skill($skill[lunging thrust-smack]);
					}
					else
					{
						if (!ThrowJunkItem())
						{
							if (dbug) { print("Aborting - you must finish.  Two rounds left!"); }
							cli_execute("abort");
						}
					}
				}
			}
		}
	}
}

void FinishHimNow(monster eek)
//Finish ASAP, but using stream if enemy is noodled, and LTS in preference to TS
{
	if (have_skill($skill[immaculate seasoning]) &&	CheckSkill($skill[stream of sauce])==0)
	{
		if (gbEnemyNoodled)
		{
			if (bVerbose) { Print("Enemy Noodled - finish with Stream"); }
			use_skill($skill[stream of sauce]);
		}
		else
		{
			if (CheckPhysicalResistance(eek))
			{
				if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
				use_skill($skill[stream of sauce]);
			}
		}
	}
	else
	{
		#check for melee weapon
		if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0)
		{
			if (bVerbose) { Print("Finish with LTS"); }
			use_skill($skill[lunging thrust-smack]);
		}
		else
		{
			if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
			{
				if (bVerbose) { Print("Finish with Stream"); }
				use_skill($skill[stream of sauce]);
			}
			else
			{
				if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0)
				{
					if (bVerbose) { Print("Finish with TS"); }
					use_skill($skill[thrust-smack]);
				}
				else
				{
					if (!ThrowJunkItem())
					{
						if (dbug) { print("Aborting - you must finish!"); }
						cli_execute("abort");
					}
				}
			}
		}
	}
}

void DontHurtHim(monster eek)
{
	if (bVerbose) { Print("Attack and miss"); }

	if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
	{
	    throw_item($item[anti-anti-antidote]);
	}
	else
	{
		if (item_amount($item[dictionary])>0 && !gbInValley)
		{
			throw_item($item[dictionary]);
		}
		else
		{
			if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
			{
				throw_item($item[facsimile dictionary]);
			}
			else
			{
				if (have_skill($skill[shake hands]) )
				{
				use_skill($skill[shake hands]);
				}
				else
				{
					if (buffed_hit_stat() - (monster_base_attack(eek)+ monster_level_adjustment()
						- giCurDelevel) < -5)
				{
					#attack and miss
					attack();
				}
				else
				//nothing we can do except throw junk, or attack and hit
				{
					if (!ThrowJunkItem())
						attack();
				}
				}

			}
		}
	}
}


void main(int iRound, monster eek, string sText)
{
    int iDamTaken=0;
    string sTemp;
    string sName;
    int iStart=0;
    int iEnd=0;

    boolean bUsedNoodles;
    string sAction;

	if(eek==zilch)
	{
       // Try to figure out what we're fighting ... gets around relay browser bug
       iStart = index_of(sText, "You're fighting " )+1;
       if (iStart > 0)
       {
      		iEnd = index_of(sText, "</span>", iStart);
       }
       if (iStart > 0 && iEnd > 0)
       {
		  sTemp = substring(sText,iStart+34,iEnd );
		  if (dbug) { print("PARSED MONSTER NAME: " + sTemp); }
		  int iCutoff = index_of(sTemp," ");
		  sName = substring(sTemp,iCutoff+1);
       }
       eek = string_to_monster(sName);
    }

	giRound = iRound;
	print ("********************************");
    print("Round " + iRound + " vs " + eek);
	#print(sText);
	print ("++++++++++++++++++++++++++++++++");

	gbInValley = (my_location() == $location[valley beyond orc chasm]);
	if (dbug) { print("Valley=" + boolean_to_string(gbInValley)); }

	if (ShieldEquipped() && have_skill($skill[hero of the half-shell]) &&
		( my_buffedstat($stat[muscle]) > my_buffedstat($stat[moxie]) ) )
	{
		gDefenceStat = $stat[muscle];
		if (dbug) { print("Hero!"); }
	}
	else
	{
		gDefenceStat = $stat[moxie];
		if (dbug) { print("Zero :("); }
	}

    if (iRound == 1)
    {
    	giCurDelevel = 0;
    	set_property("IllStasis_Delevel", "0");
    	set_property("IllStasis_StartMP", my_mp());
    	set_property("IllStasis_StartHP", my_hp());
    	bUsedNoodles = false;
    	set_property("IllNeoStasis_UsedNoodles", "false");
    	gbEnemyNoodled = false;
    	set_property("IllNeoStasis_EnemyNoodled", "false");
    }
    else
    {
		giCurDelevel = string_to_int(get_property("IllStasis_Delevel"));
		//if (dbug) { print("giCurDelevel=" + giCurDelevel); }
		bUsedNoodles = string_to_boolean(get_property("IllNeoStasis_UsedNoodles"));
		gbEnemyNoodled = string_to_boolean(get_property("IllNeoStasis_EnemyNoodled"));
    }

	//Check the combat state, eg Noodles, damage taken etc

	//if (dbug) { print("Parsing damage"); }
	iStart = index_of(sText, "You lose " );
	if (iStart > 0)
	{
		iEnd = index_of(sText, " hit point", iStart );
	}
	if (dbug) { print("iStart = " + iStart + "iEnd = " + iEnd); }
	if (iStart > 0 && iEnd > 0)
	{
		sTemp = substring(sText,iStart+9,iEnd );
		iDamTaken = string_to_int(sTemp);
		#if (dbug) { print("<color=\"red\">Took Dam:  " + iDamTaken + "</color>"); }
		if (dbug) { print("Took Dam:  " + iDamTaken); }
	}

	iStart = 0;
	iStart = index_of(sText, MSG_NOODLEBREAK);
	if (iStart > 0)
	{
		bUsedNoodles = false;
		set_property("IllNeoStasis_UsedNoodles", "false");
	}

	//Decide on our action, in reverse priority order (ie panic=last)

	sAction = "Miss"; //Default

	if (MonsterCannotHit(eek))
	{
		if (dbug) { print("Monster cannot hit..."); }

		if (PlayerCannotHit(eek))
		{
			if (dbug) { print("We cannot hit either...finish"); }
			sAction = "Finish";
		}
		else
		{
			sAction = "Attack";
		}
	}

	int iHPPct;
	iHPPct = 100*my_hp()/my_maxhp();
	if (dbug) { print("HP = " + iHPPct + "%  (HP=" + my_hp() + "/" + my_maxhp() + ")"); }
	if (iHPPct  <= HEALTH_PCT)
	{
		//We're hurting, heal if no higher priority action
		if (CheckSkill($skill[saucy salve]) == 0)
		{
			sAction = "Salve";
		}
	}

	if (giRound == 1 && my_primestat()==$stat[moxie] && index_of(sText, "You get the jump") >= 0)
	{
		sAction = "Pickpocket";
	}

	if (iDamTaken >= DAM_THRESHOLD)
	//TODO
	//Too painful!  End fight quickly
	{
		sAction = "Finish";
	}
	//else
	//{

	//}

	if (ExpectedMonsterDamage(eek) > (my_hp() + 10))
	//danger of death - panic
	//TODO
	{
		if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

		if (gbEnemyNoodled)
		{
			if (iHPPct <= HEALTH_PCT)
			{
				//heal back to safety level
				if (CheckSkill($skill[saucy salve]) == 0)
				{
					sAction = "Salve";
				}
			}
			else
			{
				sAction = "FinishNOW";
			}

		}
		else
		{
			if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
			{
				sAction = "Noodle";
			}
			else
			{
				sAction = "FinishNOW";
			}
		}
	}

    if (iRound >= 29)
    {
	    #long enough!
		sAction = "Finish";
	}

	ShowStatusMsg("Action = " + sAction);

	//And finally, perform our action
	if (sAction == "Attack") attack();
	if (sAction == "FinishNOW") FinishHimNOW(eek);
	if (sAction == "Finish") FinishHim(eek);
	if (sAction == "Salve") use_skill($skill[saucy salve]);
	if (sAction == "Miss") DontHurtHim(eek);
	if (sAction == "Pickpocket") Pickpocket();
	if (sAction == "Noodle")
	{
    	set_property("IllNeoStasis_UsedNoodles", "true");
    	set_property("IllNeoStasis_EnemyNoodled", "true");
		use_skill($skill[entangling noodles]);
	}

}
 

BDrag0n

Member
Here's a modified version, intended to change the min/max values when Salve is cast - min value of the expected monster damage + possible fumble damage + user defined safety net, and a max of my_maxhp()-15 (which can't go below the min). The intention is to reduce salving unnecessarily, and get a bit more out of out of combat healing.

Code:
#  NeoStasis.ash

#  v1.5
#  by Illarion, Mar 2007
#  A KoL Stasis script for the post-noodle world.
#  formulae courtesy of HCO forums
#  Many thanks to Holatuwol and Veracity, without whom this never would have been possible
#  Thanks to dirkdiggler for leatherback formula

#  Change history
# v1.5 changes by BDrag0n - closer min and max healing levels for salve/cocoon
#  v1.4 - better cocoon support, better handling of situations when mob or player cannot hit,
#   fixed panic noodling, included Gemelli's workaround for undefined monsters
#  v1.3 - fixed entangling noodle bug?, FinishNOW implemented, aware of physical-resistant monsters,
#   sanity check when monster can't hit player.
#  v1.2- fixed shield/hero test bug, tidied display messages, used antidote
#  v1.1 - Location checking for dictionary, pickpocket, fixed mp_cost check
#  v1.0 - Forked from ASHStasis v1.4

#TODO
#Monster variance
#abort on specific monsters

#*** Begin user-configurable variables ***#

//Change to false if antidote is changed!
boolean USE_ANTIDOTE = true;
//boolean USE_ANTIDOTE = false;

#Default values:
int DAM_THRESHOLD = 20;

// This section is about healing. The safety net is how much HP you want spare - remember to allow for monster variance!
int SAFETY_NET = 3;            //Minimum health left after expected monster damage & risk of fumble  - suggested 3
int OOC_HEAL_LEVEL = 90;       //the max HP level at which changing to out of combat healing would be better

//if your moxie exceeds monster attack by this much, don't bother trying to stasis (as mob can't hit)
int OVERMOXIE_LEVEL = 8;

#print debug messages.  These go to the Mafia status bar, and the gCLI.  Turn them off if
#you're happy with the performance, and don't care about tweaking/understanding what's going on.
boolean dbug = true;

//Use to show more detailed output
boolean bVerbose = true;

#*** End of user-configurable variables ***#

#Used for status messages
int giRound;

int giCurDelevel;
boolean gbEnemyNoodled;
boolean gbInValley;
stat gDefenceStat;
monster zilch=$monster[];

string MSG_NOODLEBREAK = "Your opponent struggles free of your entangling noodles.";

int max(int a, int b)
{
   int iRetVal;
    if (a > b)
    {
      iRetVal = a;
    }
    else
    {
       iRetVal = b;
    }
    return iRetVal;
}

int min(int a, int b)
{
   int iRetVal;
    if (a < b)
    {
      iRetVal = a;
    }
    else
    {
       iRetVal = b;
    }
    return iRetVal;
}

int LeatherBackDR()
{
   //DR from Leatherback, as dirkdiggler's formula:
   //Leatherback DR = Clamp[ 1 < ceil( (level-3)/2 ) ]

   int iResult;
   if (have_skill($skill[skin of the leatherback]))
   {
      iResult = (max(1, ceil( (my_level()-3)/2 )));
   }
   else
   {
      iResult = 0;
   }
   return iResult;
}

// max fumble damage if you're "hitting" with a weapon
int get_fumble()
{
int damage = ceil(get_power(current_equipment($slot[weapon]))/10);         // expected fumble damage for the equipped weapon
if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
  //if you're not hitting with a weapon, you won't fumble
 { damage = 0; }
if (item_amount($item[dictionary])>0 && !gbInValley)
 { damage = 0; }
if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
 { damage = 0; }
if (have_skill($skill[shake hands]) )
 { damage = 0; }
return damage;
}

void ShowStatusMsg(string Msg)
{
   print( "Round: " + giRound + " " +
   "(MP Change=" + (my_mp()-string_to_int(get_property("IllStasis_StartMP"))) +                  ", HP Change=" + (my_hp()-string_to_int(get_property("IllStasis_StartHP"))) +  ") --> " + Msg );
}

boolean ShieldEquipped()
{
   boolean bFoundShield = false;
   string sItem;
   sItem = item_to_string(current_equipment($slot[off-hand]));
   bFoundShield = bFoundShield || (index_of(sItem, "buckler" )>=0);
   bFoundShield = bFoundShield || (index_of(sItem, "shield" )>=0);
   bFoundShield = bFoundShield || (index_of(sItem, "hors d'oeuvre tray" )>=0);
   bFoundShield = bFoundShield || (index_of(sItem, "box turtle" )>=0);
   bFoundShield = bFoundShield || (index_of(sItem, "coffin lid" )>=0);
   bFoundShield = bFoundShield || (index_of(sItem, "sewer turtle" )>=0);

   if (bVerbose) { Print("Offhand=" + sItem + "-> Shield=" + boolean_to_string(bFoundShield)); }

   return bFoundShield;
}

int CheckSkill(skill myskill)
//return 0=success, 1=insufficient mana, 2=do not have skill
{
    if (!have_skill(myskill))
    {
       return 2;
    }
    else
    {
      if (my_mp() < mp_cost(myskill) )
      {
          return 1;
      }
   }
   return 0;
}

boolean CheckPhysicalResistance(monster eek)
{
   if (eek == $monster[chalkdust wraith])
      return true;
   if (eek == $monster[Ghost Miner])
      return true;
   if (eek == $monster[Snow Queen])
      return true;
   return false;
}

int ExpectedMonsterDamage(monster eek)
//BaseMonsterDmg = (20% to 25%)*Atk + (Atk-Mox)
//Dam = (Base - DR) * DA * ER
{
      int iDam;
      int iAtk;
      int iMoxPenalty;
      int iDR;
     iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;

     iMoxPenalty = max(iAtk - my_buffedstat(gDefenceStat),0);
     iDam = 0.25*iAtk + iMoxPenalty;
     iDam = iDam - damage_reduction()-LeatherbackDR();
     iDam = iDam - (iDam * damage_absorption_percent()/100);

     //TODO elemental resistance

     if (bVerbose) { Print("Expected:  (25% of " + iAtk + "+" + iMoxPenalty + "-(" + damage_reduction() + "+" + LeatherBackDR() + ")DR) * " + damage_absorption_percent() + "%DA = " + iDam); }

     if(eek==zilch)
     {
      if (dbug) { Print("Monster undefined ... assuming a reasonable range."); }
      iDam=10;
     }

     return iDam;
}

boolean MonsterCannotHit(monster eek)
//Test if mob can hit us
{
   int iAtk;
   iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;
   return ((iAtk + OVERMOXIE_LEVEL) <= my_buffedstat($stat[moxie]) );
}

boolean PlayerCannotHit(monster eek)
//Test if we can hit the mob
{
   int iDef;
   iDef = monster_base_defense(eek)+ monster_level_adjustment() - giCurDelevel;
   return (my_buffedstat(current_hit_stat()) < (iDef + 7)); //TODO check the 7
}

void PickPocket()
{
   print("Picking pocket...");
   visit_url("fight.php?action=steal");

}

boolean ThrowJunkItem()
{
   item JunkItem;
   JunkItem = $item[scroll of turtle summoning];
   if (bVerbose) { Print("Trying to throw junk..."); }

   if (item_amount(JunkItem)>0)
   {
      throw_item(JunkItem);
      return true;
   }
   JunkItem = $item[seal tooth];
   if (item_amount(JunkItem)>0)
   {
      throw_item(JunkItem);
      return true;
   }
   JunkItem = $item[spices];
   if (item_amount(JunkItem)>0)
   {
      throw_item(JunkItem);
      return true;
   }

   if (bVerbose) { Print("Failed to throw junk."); }
   return false;
}

void FinishHim(monster eek)
{
   if (dbug) { print("Finish Him!"); }
   if (gbInValley)
   //Best to use a dictionary if we can - free
   {
      if (item_amount($item[dictionary])>0)
      {
         if (bVerbose) { Print("Finish with dictionary"); }
         throw_item($item[dictionary]);
      }
      else
      {
         if (item_amount($item[facsimile dictionary])>0)
         {
            if (bVerbose) { Print("Finish with dictionary"); }
            throw_item($item[facsimile dictionary]);
         }
      }
   }
   else
   {
      if (CheckPhysicalResistance(eek) && have_skill($skill[immaculate seasoning]) &&       CheckSkill($skill[stream of sauce])==0 )
      {
         if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
         use_skill($skill[stream of sauce]);
      }
      else
      {
         #check for melee weapon
         if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0)
         {
            if (bVerbose) { Print("Finish with TS"); }
            use_skill($skill[thrust-smack]);
         }
         else
         {
            if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
            {
               if (bVerbose) { Print("Finish with Stream"); }
               use_skill($skill[stream of sauce]);
            }
            else
            {
               if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0)
               {
                  if (bVerbose) { Print("Finish with LTS"); }
                  use_skill($skill[lunging thrust-smack]);
               }
               else
               {
                  if (!ThrowJunkItem())
                  {
                     if (dbug) { print("Aborting - you must finish.  Two rounds left!"); }
                     cli_execute("abort");
                  }
               }
            }
         }
      }
   }
}

void FinishHimNow(monster eek)
//Finish ASAP, but using stream if enemy is noodled, and LTS in preference to TS
{
   if (have_skill($skill[immaculate seasoning]) &&   CheckSkill($skill[stream of sauce])==0)
   {
      if (gbEnemyNoodled)
      {
         if (bVerbose) { Print("Enemy Noodled - finish with Stream"); }
         use_skill($skill[stream of sauce]);
      }
      else
      {
         if (CheckPhysicalResistance(eek))
         {
            if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
            use_skill($skill[stream of sauce]);
         }
      }
   }
   else
   {
      #check for melee weapon
      if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0)
      {
         if (bVerbose) { Print("Finish with LTS"); }
         use_skill($skill[lunging thrust-smack]);
      }
      else
      {
         if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
         {
            if (bVerbose) { Print("Finish with Stream"); }
            use_skill($skill[stream of sauce]);
         }
         else
         {
            if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0)
            {
               if (bVerbose) { Print("Finish with TS"); }
               use_skill($skill[thrust-smack]);
            }
            else
            {
               if (!ThrowJunkItem())
               {
                  if (dbug) { print("Aborting - you must finish!"); }
                  cli_execute("abort");
               }
            }
         }
      }
   }
}

void DontHurtHim(monster eek)
{
   if (bVerbose) { Print("Attack and miss"); }

   if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
   {
       throw_item($item[anti-anti-antidote]);
   }
   else
   {
      if (item_amount($item[dictionary])>0 && !gbInValley)
      {
         throw_item($item[dictionary]);
      }
      else
      {
         if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
         {
            throw_item($item[facsimile dictionary]);
         }
         else
         {
            if (have_skill($skill[shake hands]) )
            {
            use_skill($skill[shake hands]);
            }
            else
            {
               if (buffed_hit_stat() - (monster_base_attack(eek)+ monster_level_adjustment()
                  - giCurDelevel) < -5)
            {
               #attack and miss
               attack();
            }
            else
            //nothing we can do except throw junk, or attack and hit
            {
               if (!ThrowJunkItem())
                  attack();
            }
            }

         }
      }
   }
}


void main(int iRound, monster eek, string sText)
{
    int iDamTaken=0;
    string sTemp;
    string sName;
    int iStart=0;
    int iEnd=0;

    boolean bUsedNoodles;
    string sAction;

    //Sets appropriate health levels
    int MIN_HEAL_THRESHOLD = ExpectedMonsterDamage(eek) + get_fumble()+ SAFETY_NET;  //lowest level to heal at
    int HEAL_THRESHOLD;
    if(my_maxhp()<OOC_HEAL_LEVEL)
    {
     HEAL_THRESHOLD = my_maxhp()-15;
     if(have_effect($effect[purple tongue]) > 0)        // allow for purple tongue healing
     { HEAL_THRESHOLD = HEAL_THRESHOLD-20; }
     if(have_effect($effect[Heart of Orange]) > 0)  // Allow for orange heart healing
     { HEAL_THRESHOLD = HEAL_THRESHOLD-6; }
     if(HEAL_THRESHOLD < MIN_HEAL_THRESHOLD)      // don't want to drop below the minimum health!
     { HEAL_THRESHOLD = MIN_HEAL_THRESHOLD; }
    }
    else {
     HEAL_THRESHOLD = MIN_HEAL_THRESHOLD;      // So if your max health is above the OOC_HEAL_THRESHOLD you want to allow minimal health at the end of battle
    }

   if(eek==zilch)
   {
       // Try to figure out what we're fighting ... gets around relay browser bug
       iStart = index_of(sText, "You're fighting " )+1;
       if (iStart > 0)
       {
            iEnd = index_of(sText, "</span>", iStart);
       }
       if (iStart > 0 && iEnd > 0)
       {
        sTemp = substring(sText,iStart+34,iEnd );
        if (dbug) { print("PARSED MONSTER NAME: " + sTemp); }
        int iCutoff = index_of(sTemp," ");
        sName = substring(sTemp,iCutoff+1);
       }
       eek = string_to_monster(sName);
    }

   giRound = iRound;
   print ("********************************");
    print("Round " + iRound + " vs " + eek);
   #print(sText);
   print ("++++++++++++++++++++++++++++++++");

   gbInValley = (my_location() == $location[valley beyond orc chasm]);
   if (dbug) { print("Valley=" + boolean_to_string(gbInValley)); }

   if (ShieldEquipped() && have_skill($skill[hero of the half-shell]) &&
      ( my_buffedstat($stat[muscle]) > my_buffedstat($stat[moxie]) ) )
   {
      gDefenceStat = $stat[muscle];
      if (dbug) { print("Hero!"); }
   }
   else
   {
      gDefenceStat = $stat[moxie];
      if (dbug) { print("Zero :("); }
   }

    if (iRound == 1)
    {
       giCurDelevel = 0;
       set_property("IllStasis_Delevel", "0");
       set_property("IllStasis_StartMP", my_mp());
       set_property("IllStasis_StartHP", my_hp());
       bUsedNoodles = false;
       set_property("IllNeoStasis_UsedNoodles", "false");
       gbEnemyNoodled = false;
       set_property("IllNeoStasis_EnemyNoodled", "false");
    }
    else
    {
      giCurDelevel = string_to_int(get_property("IllStasis_Delevel"));
      //if (dbug) { print("giCurDelevel=" + giCurDelevel); }
      bUsedNoodles = string_to_boolean(get_property("IllNeoStasis_UsedNoodles"));
      gbEnemyNoodled = string_to_boolean(get_property("IllNeoStasis_EnemyNoodled"));
    }

   //Check the combat state, eg Noodles, damage taken etc

   //if (dbug) { print("Parsing damage"); }
   iStart = index_of(sText, "You lose " );
   if (iStart > 0)
   {
      iEnd = index_of(sText, " hit point", iStart );
   }
   if (dbug) { print("iStart = " + iStart + "iEnd = " + iEnd); }
   if (iStart > 0 && iEnd > 0)
   {
      sTemp = substring(sText,iStart+9,iEnd );
      iDamTaken = string_to_int(sTemp);
      #if (dbug) { print("<color=\"red\">Took Dam:  " + iDamTaken + "</color>"); }
      if (dbug) { print("Took Dam:  " + iDamTaken); }
   }

   iStart = 0;
   iStart = index_of(sText, MSG_NOODLEBREAK);
   if (iStart > 0)
   {
      bUsedNoodles = false;
      set_property("IllNeoStasis_UsedNoodles", "false");
   }

   //Decide on our action, in reverse priority order (ie panic=last)

   sAction = "Miss"; //Default

   if (MonsterCannotHit(eek))
   {
      if (dbug) { print("Monster cannot hit..."); }

      if (PlayerCannotHit(eek))
      {
         if (dbug) { print("We cannot hit either...finish"); }
         sAction = "Finish";
      }
      else
      {
         sAction = "Attack";
      }
   }

    if (my_hp() < HEAL_THRESHOLD)
    {
        //We're hurting, heal if no higher priority action
          if (CheckSkill($skill[saucy salve]) == 0)
          {
             sAction = "Salve";
          }
    }

   if (giRound == 1 && my_primestat()==$stat[moxie] && index_of(sText, "You get the jump") >= 0)
   {
      sAction = "Pickpocket";
   }

   if (iDamTaken >= DAM_THRESHOLD)
   //TODO
   //Too painful!  End fight quickly
   {
      sAction = "Finish";
   }
   //else
   //{

   //}

   if (ExpectedMonsterDamage(eek) > (my_hp() + 10))
   //danger of death - panic
   //TODO
   {
      if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

      if (gbEnemyNoodled)
      {
          if (my_hp() < HEAL_THRESHOLD)
          {
              //We're hurting, heal if no higher priority action
                if (CheckSkill($skill[saucy salve]) == 0)
                {
                   sAction = "Salve";
                }
          }
         else
         {
            sAction = "FinishNOW";
         }

      }
      else
      {
         if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
         {
            sAction = "Noodle";
         }
         else
         {
            sAction = "FinishNOW";
         }
      }
   }

    if (iRound >= 29)
    {
       #long enough!
      sAction = "Finish";
   }

   ShowStatusMsg("Action = " + sAction);

   //And finally, perform our action
   if (sAction == "Attack") attack();
   if (sAction == "FinishNOW") FinishHimNOW(eek);
   if (sAction == "Finish") FinishHim(eek);
   if (sAction == "Salve") use_skill($skill[saucy salve]);
   if (sAction == "Miss") DontHurtHim(eek);
   if (sAction == "Pickpocket") Pickpocket();
   if (sAction == "Noodle")
   {
       set_property("IllNeoStasis_UsedNoodles", "true");
       set_property("IllNeoStasis_EnemyNoodled", "true");
      use_skill($skill[entangling noodles]);
   }

}
 

illarion

Member
BDragon, sorry for the delay looking at your changes. It sounds solid in principle, I just haven't had a moment to go over it (and won't this coming week very likely - looking very hectic).

I really appreciate your interest and help though!

Take care,
I.
 

gemelli

Member
I'm a huge fan of this script. I wanted to share an update that I added, incorporating BDrag0n's changes as well as some other new functions:

# v1.6 - changes by Gemelli - added check to see if monster is safe to use stasis against, added
# immediate-finish logic for unsafe monsters, tweaked logic of FinishNOW routine, added
# "we have enough MP" logic
# v1.5 - changes by BDrag0n - closer min and max healing levels for salve/cocoon

Please let me know if you have any questions or comments. And please don't consider this to be an actual version update unless you hear so directly from Illarion :)
 

Attachments

  • NeoStasis.ash.txt
    22.3 KB · Views: 122

BDrag0n

Member
Ooh, nice. Thanks Gemelli, I like these additions.

I've modified the script again, to include a damage-tracking mechanism suggested by Darth Dud - basically for the first 5 rounds of a fight the expected damage is calculated using the formula, but after that it will use the max damage received so far as the expected damage next round.

I've also tweaked handling the procrastination giant - he's safe to stasis, but not to hit. So this version will allow you to stasis him, but where necessary will try to blast him with spells, or cast CLEESH (personal preference, I'd rather just kill him than convert him).

I've also changed the FinishHimNow to use SauceKill rather than just Storm or Stream. Preliminary testing shows everything OK, but that's based on less than 1 day testing so far :)

Code:
#  NeoStasis.ash
#  v1.7
#  by Illarion, Mar 2007 (w/input from BDrag0n and Gemelli)
#  A KoL Stasis script for the post-noodle world.
#  formulae courtesy of HCO forums
#  Many thanks to Holatuwol and Veracity, without whom this never would have been possible
#  Thanks to dirkdiggler for leatherback formula

#  Change history

#  v1.7 - (13/04/07) changes by BDrag0n - added new expected damage routine suggested by DarthDud, changed procrastination giant handling
#  v1.6 - changes by Gemelli - added check to see if monster is safe to use stasis against, added 
#       immediate-finish logic for unsafe monsters, tweaked logic of FinishNOW routine, added 
# 	"we have enough MP" logic
#  v1.5 - changes by BDrag0n - closer min and max healing levels for salve/cocoon
#  v1.4 - better cocoon support, better handling of situations when mob or player cannot hit,
#	fixed panic noodling, included Gemelli's workaround for undefined monsters
#  v1.3 - fixed entangling noodle bug?, FinishNOW implemented, aware of physical-resistant monsters,
#	sanity check when monster can't hit player.
#  v1.2- fixed shield/hero test bug, tidied display messages, used antidote
#  v1.1 - Location checking for dictionary, pickpocket, fixed mp_cost check
#  v1.0 - Forked from ASHStasis v1.4

#TODO
#Monster variance
#abort on specific monsters


#*** Begin user-configurable variables ***#

//Change to false if antidote is changed!
boolean USE_ANTIDOTE = true;
//boolean USE_ANTIDOTE = false;

#Default values:
int DAM_THRESHOLD = 20;

// This section is about healing. The safety net is how much HP you want spare to allow for monster variance!
int SAFETY_NET = 3;            //Minimum health left after expected monster damage & risk of fumble  - suggested 3.
int OOC_HEAL_LEVEL = 80;       //the max HP level at which changing to out of combat healing would be better

// Threshhold for painful combat effects: set to high (only abort on monsters with really bad
// effects), medium (abort on monsters with moderate-to-bad effects), or low (abort on all 
// monsters that could give a bad combat effect)
string EFFECT_THRESHOLD = "medium";

// Set threshhold for MP ... if you are more than MANA_PCT % the script will stop
int MANA_PCT = 90;

// And since the good ol' Bugbear eats MP like it was Pringles ...
if(my_familiar()==$familiar[Pygmy Bugbear Shaman]) MANA_PCT=100;

//if your moxie exceeds monster attack by this much, don't bother trying to stasis (as mob can't hit)
int OVERMOXIE_LEVEL = 8;

#print debug messages.  These go to the Mafia status bar, and the gCLI.  Turn them off if
#you're happy with the performance, and don't care about tweaking/understanding what's going on.
boolean dbug = true;

//Use to show more detailed output
boolean bVerbose = true;

#*** End of user-configurable variables ***#

#Used for status messages
int giRound;

int giCurDelevel;
boolean gbEnemyNoodled;
boolean gbInValley;
stat gDefenceStat;
monster zilch=$monster[];

string MSG_NOODLEBREAK = "Your opponent struggles free of your entangling noodles.";

int max(int a, int b)
{
	int iRetVal;
 	if (a > b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int min(int a, int b)
{
	int iRetVal;
 	if (a < b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int LeatherBackDR()
{
	//DR from Leatherback, as dirkdiggler's formula:
	//Leatherback DR = Clamp[ 1 < ceil( (level-3)/2 ) ]

	int iResult;
	if (have_skill($skill[skin of the leatherback]))
	{
		iResult = (max(1, ceil( (my_level()-3)/2 )));
	}
	else
	{
		iResult = 0;
	}
	return iResult;
}

void ShowStatusMsg(string Msg)
{
	print( "Round: " + giRound + " " +
	"(MP Change=" + (my_mp()-string_to_int(get_property("IllStasis_StartMP"))) +      				", HP Change=" + (my_hp()-string_to_int(get_property("IllStasis_StartHP"))) +  ") --> " + Msg );
}

boolean ShieldEquipped()
{
	boolean bFoundShield = false;
	string sItem;
	sItem = item_to_string(current_equipment($slot[off-hand]));
	bFoundShield = bFoundShield || (index_of(sItem, "buckler" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "shield" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "hors d'oeuvre tray" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "box turtle" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "coffin lid" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "sewer turtle" )>=0);

	if (bVerbose) { Print("Offhand=" + sItem + "-> Shield=" + boolean_to_string(bFoundShield)); }

	return bFoundShield;
}

int CheckSkill(skill myskill)
//return 0=success, 1=insufficient mana, 2=do not have skill
{
    if (!have_skill(myskill))
    {
       return 2;
    }
    else
    {
		if (my_mp() < mp_cost(myskill) )
		{
		   return 1;
		}
	}
	return 0;
}

// max fumble damage if you're "hitting" with a weapon
int get_fumble()
{
	// expected fumble damage for the equipped weapon
	int damage = ceil(get_power(current_equipment($slot[weapon]))/10);         
	if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
	  //if you're not hitting with a weapon, you won't fumble
		 { damage = 0; }
	if (item_amount($item[dictionary])>0 && !gbInValley)
		 { damage = 0; }
	if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
		 { damage = 0; }
	if (have_skill($skill[shake hands]) )
		 { damage = 0; }
	return damage;
}


string StasisSafe(monster eek) {

	if(EFFECT_THRESHOLD=="low") {
	
		// Trigger on low-impact effects, which generally affect a single stat
		if (eek == $monster[7-foot dwarf foreman])
			// Grilled
			return "finish";
		if (eek == $monster[disease-in-the-box])
			// The Disease
			return "finish";
		if (eek == $monster[decent lumberjack])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjack supervisor])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjill])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjuan])
			// axe wound
			return "finish";
		if (eek == $monster[conjoined zmombie])
			// half-eaten brain
			return "finish";
		if (eek == $monster[corpulent zobmie])
			// half-eaten brain
			return "finish";
		if (eek == $monster[grave rober zmobie])
			// half-eaten brain
			return "finish";
		if (eek == $monster[zombie waltzers])
			// half-eaten brain
			return "finish";
		if (eek == $monster[zmobie])
			// half-eaten brain
			return "finish";
		if (eek == $monster[big creepy spider])
			// hardly poisoned
			return "finish";
		if (eek == $monster[completely different spider])
			// hardly poisoned
			return "finish";
		if (eek == $monster[mayonnaise wasp])
			// hardly poisoned
			return "finish";
		if (eek == $monster[mind flayer])
			// confused
			return "finish";

	}

	if(EFFECT_THRESHOLD=="medium") {
	
		// Trigger on medium-impact effects: Moderate poisonings, etc.
		if (eek == $monster[acid blob])
			// corroded weapon
			return "sauce";
		if (eek == $monster[whitesnake])
			// a little bit poisoned
			return "finish";
		if (eek == $monster[dodecapede])
			// a little bit poisoned
			return "finish";
		if (eek == $monster[spectral jellyfish])
			// somewhat poisoned
			return "finish";
		if (eek == $monster[swarm of killer bees])
			// somewhat poisoned
			return "finish";

	}

	// Always trigger on high-impact effects

	if (eek == $monster[crusty hippy])
		// socialismydia
		return "finishnow";
//	if (eek == $monster[procrastination giant])  // dealt with elsewhere - can stasis OK, just not hit it!
//		// cunctatitis
//		return "cleesh";
	if (eek == $monster[protagonist])
		// temporary amnesia
		return "finishnow";
	if (eek == $monster[quantum mechanic])
		// teleportitis
		return "finishnow";
	if (eek == $monster[empty suit of armor])
		// tetanus
		return "finishnow";

	// If we got here, no worries!  Stasis away, you filthy stasis monkey.
	
	return "safe";

}


void SauceKill(monster eek) {

	// Guess at monster remaining HP, see which spell we have that can do as close 
	// to a one-hit kill as possible
	
	string whichSpell="";
	
	int my_myst=my_buffedstat($stat[mysticality]);
	int hp_target=monster_base_hp(eek)+monster_level_adjustment();
	if(dbug) print("SauceKill: need to do " + hp_target + " points of damage in one turn.");
	
	// Sauceror spells
	int streamdam=min((my_myst/10)+3,18);	// 3 mp
	int stormdam=min((my_myst/5)+14,39);	// 12 mp
	int wavedam=min((my_myst*0.3)+20,50);	// 23 mp
	int geyserdam=(my_myst*0.35)+35;	// 40 mp

	// Pastamancer spells
	int minordam=min((my_myst*.07)+3,18);	// 4 mp
	int extremedam=min((my_myst*.15)+8,28);	// 7 mp
	int conedam=min((my_myst*.25)+16,46);	// 19 mp
	int weapdam=(my_myst*.35)+32;		// 35 mp
	
	// Spell bonuses
	int spicebonus=0;
	int expectdam=0;
	if(have_skill($skill[intrinsic spiciness])) spicebonus=min(my_level(),10);
	int immaculatebonus=0;
	if(have_skill($skill[immaculate seasoning])) immaculatebonus=10;
	
	
	// Check all spells in descending order of MP cost
	// Added check for enough MP to cast - BDrag0n
	if(CheckSkill($skill[saucegeyser])==0) {
		if(geyserdam+spicebonus+immaculatebonus>=hp_target && my_mp()>mp_cost($skill[saucegeyser])) {
			whichSpell="saucegeyser";
			expectdam=geyserdam+spicebonus+immaculatebonus;
		}
	}
	if(CheckSkill($skill[weapon of the pastalord])==0 && my_mp()>mp_cost($skill[weapon of the pastalord])) {
		if(weapdam>=hp_target) {
			whichSpell="weapon of the pastalord";
			expectdam=weapdam;
		}
	}
	if(CheckSkill($skill[wave of sauce])==0) {
		if(wavedam+spicebonus+immaculatebonus>=hp_target && my_mp()>mp_cost($skill[wave of sauce])) {
			whichSpell="wave of sauce";
			expectdam=wavedam+spicebonus+immaculatebonus;
		}
	}
	if(CheckSkill($skill[cone of whatever])==0 && my_mp()>mp_cost($skill[cone of whatever])) {
		if(conedam>=hp_target) {
			whichSpell="cone of whatever";
			expectdam=conedam;
		}
	}
	if(CheckSkill($skill[saucestorm])==0) {
		if(stormdam+spicebonus+immaculatebonus>=hp_target && my_mp()>mp_cost($skill[saucestorm])) {
			whichSpell="saucestorm";
			expectdam=stormdam+spicebonus+immaculatebonus;
		}
	}
	if(CheckSkill($skill[extreme ray of something])==0 && my_mp()>mp_cost($skill[extreme ray of something])) {
		if(extremedam>=hp_target) {
			whichSpell="extreme ray of something";
			expectdam=extremedam;
		}
	}
	if(CheckSkill($skill[minor ray of something])==0 && my_mp()>mp_cost($skill[minor ray of something])) {
		if(minordam>=hp_target) {
			whichSpell="minor ray of something";
			expectdam=minordam;
		}
	}
	if(CheckSkill($skill[stream of sauce])==0 && my_mp()>mp_cost($skill[stream of sauce])) {
		if(streamdam+spicebonus+immaculatebonus>=hp_target) {
			whichSpell="stream of sauce";
			expectdam=streamdam+spicebonus+immaculatebonus;
		}
	}

	if(whichSpell=="") {
		// Hmm.  Got CLEESH?
		if(have_skill($skill[cleesh]) && my_mp()>mp_cost($skill[cleesh])) 
                { whichSpell="cleesh"; }
                else { attack(); }
	} else {
		if(dbug) print("Casting spell " + whichSpell + " for what I hope will be " + expectdam + " HP.");

		skill thisSpell=string_to_skill(whichSpell);
		use_skill(thisSpell);
	}

}



boolean CheckPhysicalResistance(monster eek)
{
	if (eek == $monster[chalkdust wraith])
		return true;
	if (eek == $monster[Ghost Miner])
		return true;
	if (eek == $monster[Snow Queen])
		return true;
	return false;
}

int ExpectedMonsterDamage(monster eek)
//BaseMonsterDmg = (20% to 25%)*Atk + (Atk-Mox)
//Dam = (Base - DR) * DA * ER
{
 	 int iDam;
 	 int iAtk;
 	 int iMoxPenalty;
 	 int iDR;
	 iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;

	 iMoxPenalty = max(iAtk - my_buffedstat(gDefenceStat),0);
	 iDam = 0.25*iAtk + iMoxPenalty;
	 iDam = iDam - damage_reduction()-LeatherbackDR();
	 iDam = iDam - (iDam * damage_absorption_percent()/100);

	 //TODO elemental resistance

	 if (bVerbose) { Print("Expected:  (25% of " + iAtk + "+" + iMoxPenalty + "-(" + damage_reduction() + "+" + LeatherBackDR() + ")DR) * " + damage_absorption_percent() + "%DA = " + iDam); }

	 if(eek==zilch)
	 {
		if (dbug) { Print("Monster undefined ... assuming a reasonable range."); }
		iDam=10;
	 }
	 return iDam;
}

boolean MonsterCannotHit(monster eek)
//Test if mob can hit us
{
	int iAtk;
	iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;
	return ((iAtk + OVERMOXIE_LEVEL) <= my_buffedstat($stat[moxie]) );
}

boolean PlayerCannotHit(monster eek)
//Test if we can hit the mob
{
	int iDef;
	iDef = monster_base_defense(eek)+ monster_level_adjustment() - giCurDelevel;
	return (my_buffedstat(current_hit_stat()) < (iDef + 7)); //TODO check the 7
}

void PickPocket()
{
	print("Picking pocket...");
	visit_url("fight.php?action=steal");

}

boolean ThrowJunkItem()
{
	item JunkItem;
	JunkItem = $item[scroll of turtle summoning];
	if (bVerbose) { Print("Trying to throw junk..."); }

	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[seal tooth];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[spices];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}

	if (bVerbose) { Print("Failed to throw junk."); }
	return false;
}


void FinishHim(monster eek)
{
	if (dbug) { print("Finish Him!"); }
	if (gbInValley)
	//Best to use a dictionary if we can - free
	{
		if (item_amount($item[dictionary])>0)
		{
			if (bVerbose) { Print("Finish with dictionary"); }
			throw_item($item[dictionary]);
		}
		else
		{
			if (item_amount($item[facsimile dictionary])>0)
			{
				if (bVerbose) { Print("Finish with dictionary"); }
				throw_item($item[facsimile dictionary]);
			}
		}
	}
	else
	{
		if (CheckPhysicalResistance(eek) && have_skill($skill[immaculate seasoning]) && 		CheckSkill($skill[stream of sauce])==0 )
		{
			if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
			use_skill($skill[stream of sauce]);
		}
		else
		{
			#check for melee weapon
			if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0)
			{
				if (bVerbose) { Print("Finish with TS"); }
				use_skill($skill[thrust-smack]);
			}
			else
			{
				if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
				{
					if (bVerbose) { Print("Finish with Stream"); }
					use_skill($skill[stream of sauce]);
				}
				else
				{
					if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0)
					{
						if (bVerbose) { Print("Finish with LTS"); }
						use_skill($skill[lunging thrust-smack]);
					}
					else
					{
						if (!ThrowJunkItem())
						{
							if (dbug) { print("Aborting - you must finish.  Two rounds left!"); }
							cli_execute("abort");
						}
					}
				}
			}
		}
	}
}

void FinishHimNow(monster eek)
//Finish ASAP, but using stream if enemy is noodled, and LTS in preference to TS
{

	#check for melee weapon - unless it's the procrastination giant, who we don't want to hit
	if (current_hit_stat() == $stat[muscle] && !CheckPhysicalResistance(eek) && eek != $monster[procrastination giant] && !gbEnemyNoodled)
	{
		if(CheckSkill($skill[lunging thrust-smack])==0) {

			// First preference: finish with LTS
			if (bVerbose) { Print("Finish with LTS"); }
			use_skill($skill[lunging thrust-smack]);
		} else {
			if(CheckSkill($skill[thrust-smack])==0) {
				// Second preference: finish with TS
				if (bVerbose) { Print("Finish with TS"); }
				use_skill($skill[thrust-smack]);
			}
		}
	}	
	else
	{
		// Either we are not holding a melee weapon, or the monster is phys resistant.
		// Spell time!
		// Changed to use SauceKill function added by Gemelli
			SauceKill(eek);
	}

}

void DontHurtHim(monster eek)
{
	if (bVerbose) { Print("Attack and miss"); }

	if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
	{
	   throw_item($item[anti-anti-antidote]);
	}
	else
	{
		if (item_amount($item[dictionary])>0 && !gbInValley)
		{
			throw_item($item[dictionary]);
		}
		else
		{
			if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
			{
				throw_item($item[facsimile dictionary]);
			}
			else
			{
				if (have_skill($skill[shake hands]) )
				{
				use_skill($skill[shake hands]);
				}
				else
				{
					if (buffed_hit_stat() - (monster_base_attack(eek)+ monster_level_adjustment()
						- giCurDelevel) < -5)
				{
					#attack and miss
					attack();
				}
				else
				//nothing we can do except throw junk, or attack and hit
				{
					if( eek !=$monster[procrastination giant] )
					{ Saucekill(eek); }
					else {
					if (!ThrowJunkItem())
					 {	attack(); }
					}					
				}
				}

			}
		}
	}
}


void main(int iRound, monster eek, string sText)
{


	int iDamTaken=0;
	string sTemp;
	string sName;
	int iStart=0;
	int iEnd=0;

	boolean bUsedNoodles;
	string sAction;



	// Gemelli workaround for monster undefined in relay browser
	if(eek==zilch)
	{
	       // Try to figure out what we're fighting ... gets around relay browser bug
	       iStart = index_of(sText, "You're fighting " )+1;
	       if (iStart > 0)
	       {
			iEnd = index_of(sText, "", iStart);
			iEnd = index_of(sText, "</span>", iStart);
	       }
	       if (iStart > 0 && iEnd > 0)
	       {
			 sTemp = substring(sText,iStart+34,iEnd );
			 int iCutoff = index_of(sTemp," ");
			 sName = substring(sTemp,iCutoff+1);
			 if (dbug) { print("PARSED MONSTER NAME: " + sName); }
	       }
	       eek = string_to_monster(sName);
	}

	// Define correct defense stat
	if (ShieldEquipped() && have_skill($skill[hero of the half-shell]) &&
		( my_buffedstat($stat[muscle]) > my_buffedstat($stat[moxie]) ) )
	{
		gDefenceStat = $stat[muscle];
	    set_property("IllNeoStasis_Hero", "true");
		// if (dbug) { print("Hero!"); }
	}
	else
	{
		gDefenceStat = $stat[moxie];
	    set_property("IllNeoStasis_Hero", "false");
		// if (dbug) { print("Zero :("); }
	}

	// Decide the expected monster damage - first 4 rounds based on calculation, 5th and on based on damage taken this fight 
	// Based on DarthDud's suggestions, added by BDrag0n
	int ExpectedDamage=ExpectedMonsterDamage(eek);
	if(iRound >= 5)
	{
		ExpectedDamage = string_to_int(get_property("IllNeoStasis_MaxDamTaken"));
                if(dbug) { Print ("Expected damage based on this fight is: "+int_to_string(ExpectedDamage)); }
	}


	//Sets appropriate health levels
	int MIN_HEAL_THRESHOLD = ExpectedDamage+get_fumble() + SAFETY_NET;  //lowest level to heal at

	int HEAL_THRESHOLD;
	if(my_maxhp()<OOC_HEAL_LEVEL)    {
		HEAL_THRESHOLD = my_maxhp()-15;
		if(have_effect($effect[purple tongue]) > 0)        // allow for purple tongue healing
			{ HEAL_THRESHOLD = HEAL_THRESHOLD-20; }
		if(have_effect($effect[Heart of Orange]) > 0)  // Allow for orange heart healing
			{ HEAL_THRESHOLD = HEAL_THRESHOLD-6; }
		if(HEAL_THRESHOLD < MIN_HEAL_THRESHOLD)      // don't want to drop below the minimum health!
			{ HEAL_THRESHOLD = MIN_HEAL_THRESHOLD; }
	} else {
		HEAL_THRESHOLD = MIN_HEAL_THRESHOLD;      // So if your max health is above the OOC_HEAL_THRESHOLD you want to allow minimal health at the end of battle
	}

	// if(dbug) print("Your heal threshhold is " + HEAL_THRESHOLD);

        int MaxDamTaken=0;   //this will be used to keep track of damage taken this fight

	// Start the round!
	giRound = iRound;
	print ("++++++++++++++++++++++++++++++++");
        print("Round " + iRound + " vs " + eek);
	#print(sText);
	print ("++++++++++++++++++++++++++++++++");

	gbInValley = (my_location() == $location[valley beyond orc chasm]);
	if (dbug) { print("Valley=" + boolean_to_string(gbInValley)); }

	if (iRound == 1)
	{
		giCurDelevel = 0;
		set_property("IllStasis_Delevel", "0");
		set_property("IllStasis_StartMP", my_mp());
		set_property("IllStasis_StartHP", my_hp());
		bUsedNoodles = false;
		set_property("IllNeoStasis_UsedNoodles", "false");
		gbEnemyNoodled = false;
		set_property("IllNeoStasis_EnemyNoodled", "false");
		set_property("IllNeoStasis_MaxDamTaken", "0");
	}
	else
	{
		giCurDelevel = string_to_int(get_property("IllStasis_Delevel"));
		//if (dbug) { print("giCurDelevel=" + giCurDelevel); }
		bUsedNoodles = string_to_boolean(get_property("IllNeoStasis_UsedNoodles"));
		gbEnemyNoodled = string_to_boolean(get_property("IllNeoStasis_EnemyNoodled"));
		MaxDamTaken=string_to_int(get_property("IllNeoStasis_MaxDamTaken"));
	}

	// Check damage taken

	iStart = index_of(sText, "You lose " );
	if (iStart > 0)
	{
		iEnd = index_of(sText, " hit point", iStart );
	}
	// if (dbug) { print("iStart = " + iStart + "iEnd = " + iEnd); }
	if (iStart > 0 && iEnd > 0)
	{
		sTemp = substring(sText,iStart+9,iEnd );
		iDamTaken = string_to_int(sTemp);
		if (dbug) { print("Took Dam:  " + iDamTaken); }
		if(iDamTaken > MaxDamTaken) { set_property("IllNeoStasis_MaxDamTaken", int_to_string(iDamTaken)); }
	}

	// Check Noodles status
	
	iStart = 0;
	iStart = index_of(sText, MSG_NOODLEBREAK);
	if (iStart > 0)
	{
		bUsedNoodles = false;
		set_property("IllNeoStasis_UsedNoodles", "false");
	}

	// Report on current HP and MP threshholds

	int iHPPct;
	iHPPct = 100*my_hp()/my_maxhp();
	int iMPPct = 100*my_mp()/my_maxmp();
	if (dbug) { print("HP = " + iHPPct + "%  (HP=" + my_hp() + "/" + my_maxhp() + ")"); }
	if (dbug) { print("MP = " + iMPPct + "%  (MP=" + my_mp() + "/" + my_maxmp() + ")"); }

	//Decide on our action, in reverse priority order

	sAction = "Miss"; //Default

	if (MonsterCannotHit(eek))
	{
		if (dbug) { print("Monster cannot hit..."); }

		if (PlayerCannotHit(eek))
		{
			if (dbug) { print("We cannot hit either...finish"); }
			sAction = "Finish";
		}
		else
		{
			sAction = "Attack";
		}
	}


	if (my_hp() < HEAL_THRESHOLD)
	{
		//We're hurting, heal if no higher priority action
		if (CheckSkill($skill[saucy salve]) == 0)
		{
			sAction = "Salve";
		}
	}

	if (giRound == 1 && my_primestat()==$stat[moxie] && index_of(sText, "You get the jump") >= 0)
	{
		sAction = "Pickpocket";
	}

	if (iDamTaken >= DAM_THRESHOLD)
	//TODO
	//Too painful!  End fight quickly
	{
		sAction = "Finish";
	}
	//else
	//{

	//}

	// Are we full on MP?  If so, we don't need to draw this out.

	if (iMPPct  >= MANA_PCT)
	{
		//
		if (dbug) { print("We're full on MP!"); }
		if (iHPPct<85 && CheckSkill($skill[saucy salve]) == 0)
		{
			// If we are below 85% HP, might as well burn some MP on healing
			sAction = "Salve";
		}
		else
		{
			sAction = "Finish";
		}
	}


	// Check to see if the monster is safe

	string safetest=StasisSafe(eek);
	if(safetest!="safe") {
		if (dbug) { print("Monster is not safe!  Result=" + safetest); }
		if(safetest=="sauce") {
			sAction="SauceKill";
		} else {
			if (safetest=="cleesh") {
				sAction="cleesh";
			} else {
				if(safetest=="finishnow") {
					sAction="FinishNOW";
				} else sAction="Finish";
			}
		}
	}


	if (ExpectedDamage > (my_hp() + 10))
	//danger of death - panic
	//TODO
	{
		if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

		if (gbEnemyNoodled)
		{
			if (my_hp() < HEAL_THRESHOLD)
			{
				//We're hurting, heal if no higher priority action
				if (CheckSkill($skill[saucy salve]) == 0)
				{
					sAction = "Salve";
				}
			}
			else
			{
				sAction = "FinishNOW";
			}

		}
		else
		{
			if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
			{
				sAction = "Noodle";
			}
			else
			{
				sAction = "FinishNOW";
			}
		}
	}


	if (iRound >= 29)
	{
	   #long enough!
		sAction = "Finish";
	}

	ShowStatusMsg("Action = " + sAction);

	//And finally, perform our action
	if (sAction == "Attack") attack();
	if (sAction == "FinishNOW") FinishHimNOW(eek);
	if (sAction == "Finish") FinishHim(eek);
	if (sAction == "Salve") use_skill($skill[saucy salve]);
	if (sAction == "Cleesh") {
		if(CheckSkill($skill[cleesh]) == 0) use_skill($skill[cleesh]);
			else sAction="SauceKill";
	}
	if (sAction == "Miss") DontHurtHim(eek);
	if (sAction == "Pickpocket") Pickpocket();
	if (sAction == "SauceKill") SauceKill(eek);
	if (sAction == "Noodle")
	{
	set_property("IllNeoStasis_UsedNoodles", "true");
	set_property("IllNeoStasis_EnemyNoodled", "true");
		use_skill($skill[entangling noodles]);
	}
}
 

gemelli

Member
This is looking really sharp; nice work!

One thing I've realized about SauceKill() is that it doesn't cope well with situations where the monster can't be killed outright with a single spell.  It often casts CLEESH when I wish it wouldn't :(  There are a few ways to handle this ... one would be to look for the best "plan B" spell available if you can't get a one-shot kill.  The other (quick-and-dirty) approach is just to fall back on Stream, like so:

Code:
	if(whichSpell=="") {
		// Go for the low-cost option; better than nothing
		if(CheckSkill($skill[stream of sauce])==0) {
			if(dbug) print("Casting stream of sauce ... which is better than nothing.");
			use_skill($skill[stream of sauce]);
		} else {
			// Hmm.  Got CLEESH?
			if(have_skill($skill[cleesh])) whichSpell="cleesh";
			else attack();
		} 
	} else {
		if(dbug) print("Casting spell " + whichSpell + " for what I hope will be " + expectdam + " HP.");

		skill thisSpell=string_to_skill(whichSpell);
		use_skill(thisSpell);
	}

Also, the relay browser bug seems to have been fixed, so the code just after "// Gemelli workaround for monster undefined in relay browser" can be removed.

In my copy of NeoStasis, I made the following changes ... they seem to work well for me, but YMMV.

Code:
HEAL_THRESHOLD = MIN_HEAL_THRESHOLD;

I found that I kept unnecessarily slipping into Panic state at this setting when using the script in the early game, so I added an extra buffer to keep things moving:

Code:
HEAL_THRESHOLD = MIN_HEAL_THRESHOLD + 10; // Added extra buffer to prevent false panic

Code:
// Check Noodles status
	
iStart = 0;
iStart = index_of(sText, MSG_NOODLEBREAK);
if (iStart > 0)
{
	bUsedNoodles = false;
	set_property("IllNeoStasis_UsedNoodles", "false");
}

I think this is a bug; if the enemy breaks out of your noodles, UsedNoodles should still be true.  What needs to be changed is EnemyNoodled:

Code:
// Check Noodles status
	
iStart = 0;
iStart = index_of(sText, MSG_NOODLEBREAK);
if (iStart > 0)
{
	gbEnemyNoodled = false;
	set_property("IllNeoStasis_EnemyNoodled", "false");  // UsedNoodles should still be true here
}

Code:
	if (ExpectedDamage > (my_hp() + 10))
	//danger of death - panic
	//TODO
	{
		if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

		if (gbEnemyNoodled)
		{
			if (my_hp() < HEAL_THRESHOLD)
			{
				//We're hurting, heal if no higher priority action
				if (CheckSkill($skill[saucy salve]) == 0)
				{
					sAction = "Salve";
				}
			}
			else
			{
				sAction = "FinishNOW";
			}

		}
		else
		{
			if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
			{
				sAction = "Noodle";
			}
			else
			{
				sAction = "FinishNOW";
			}
		}
	}

Panic Mode was giving me problems.  First, when you're playing at early levels with low max HP, the script would throw you into Panic way too often when what you really needed was just a dose of Salve to get you back up to full health. Second, this code tested whether the monster's expected damage is going to be more than 10 points what's needed to kill you -- I'm more interested in trying to stay above the safety net if possible.  Third, the script wasn't really executing the FinishNow part of the script once Noodles has been used; the "//We're hurting, heal if no higher priority action" condition was nearly always executed once we enter panic mode (since if you're in panic mode, you are almost certainly below your healing threshhold).  What I wanted was to determine if using Salve got us back above our healing threshhold ... if so, cast it; if not, finish the monster now.  The code below seems to fix all three issues.

Code:
	if (ExpectedDamage + SAFETY_NET > my_hp())
	//danger of death - panic
	//TODO
	{
		if(my_maxhp()-my_hp()<=10) {
			sAction = "Salve";
		} else {
			if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

			if (gbEnemyNoodled)
			{
				if (my_hp()+10>HEAL_THRESHOLD)
				{
					//heal back to safety level
					if (CheckSkill($skill[saucy salve]) == 0)
					{
						sAction = "Salve";
					}
				}
				else
				{
					sAction = "FinishNOW";
				}
			}
			else
			{
				if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
				{
					sAction = "Noodle";
				}
				else
				{
					sAction = "FinishNOW";
				}
			}
		}
	}

I also moved the section starting with "// Are we full on MP?  If so, we don't need to draw this out" up a little earlier in the code, just after the if (MonsterCannotHit(eek)) test.  I think that mana burning is a low priority compared to healing, and would rather the test for healing override the "full on MP" test results.

I don't think these changes are enough to warrant a full version increase; let's call this 1.7a :)  The complete file is attached below if you're interested.
 

Attachments

  • NeoStasis.ash.txt
    23.5 KB · Views: 110

BDrag0n

Member
I didn't look at the logic in SauceKill more than briefly last run-through, though I did add an MP check so you weren't trying to cast a spell you couldn't afford. Defaulting to Stream is probably the best option, since it is the cheapest, however it might be worth an extra check if 2 storms would prevent imminent death.

- For the panic state: would it be more useful to change
Code:
	if (ExpectedDamage > (my_hp() + 10))
	//danger of death - panic
to
Code:
	if (ExpectedDamage > (my_hp() + SAFETY_NET) && (my_maxHP()-my_HP() )> 15)
	//danger of death - panic
This would then allow you to specify your own safety net for the panic, and also not panic if a single cast of Salve could get you back to full health.

- The noodles thing does look like a bug, yep. Good catch :)

- The change to panic state above makes it much more likely that when you hit panic you'll be below the heal threshold so the additional check to see if 1 cast gets you safe is definitely worth it.
OK, new(ish) version - let's call it 1.7b <g>

Changes here
- I've taken the script you attached, removed the +10 to prevent panic and changed to my suggested fix.
- Altered the stasis-safe script to use spellkill against hot/cold weak mobs, if you've got immaculate & stream
- rewritten saucekill entirely - it now uses a simple record to store the information about the spells, and calls a separate function (Choose_spell) to decide which to use. If no spell will one-hit, it checks if you can 2-hit.
- if you're not 1-hitting in the saucekill, it'll noodle if you've more than 3 rounds left to save damage.

Code:
#  NeoStasis.ash
#  v1.7b
#  by Illarion, Mar 2007 (w/input from BDrag0n and Gemelli, Mar 2007)
#  A KoL Stasis script for the post-noodle world.
#  formulae courtesy of HCO forums
#  Many thanks to Holatuwol and Veracity, without whom this never would have been possible
#  Thanks to dirkdiggler for leatherback formula

#  Change history

#  v1.7b - (14/04/07) SauceKill rewritten to consider a 2-hit kill if 1-hit isn't possible. Changed behaviour for unsafe monsters which are weak against hot/cold IF you have immaculate seasoning & stream
#  v1.7a - Minor tweaks by Gemelli
#  v1.7 - (13/04/07) changes by BDrag0n - added new expected damage routine suggested by DarthDud, changed procrastination giant handling
#  v1.6 - changes by Gemelli - added check to see if monster is safe to use stasis against, added 
#       immediate-finish logic for unsafe monsters, tweaked logic of FinishNOW routine, added 
# 	    "we have enough MP" logic
#  v1.5 - changes by BDrag0n - closer min and max healing levels for salve/cocoon
#  v1.4 - better cocoon support, better handling of situations when mob or player cannot hit,
#	fixed panic noodling, included Gemelli's workaround for undefined monsters
#  v1.3 - fixed entangling noodle bug?, FinishNOW implemented, aware of physical-resistant monsters,
#	sanity check when monster can't hit player.
#  v1.2- fixed shield/hero test bug, tidied display messages, used antidote
#  v1.1 - Location checking for dictionary, pickpocket, fixed mp_cost check
#  v1.0 - Forked from ASHStasis v1.4



#*** Begin user-configurable variables ***#

//Change to false if antidote is changed!
boolean USE_ANTIDOTE = true;

//Change to false if goodfella contract is changed!
boolean USE_GOODFELLA = true;


#Default values:
int DAM_THRESHOLD = 20;

// This section is about healing. The safety net is how much HP you want spare - remember to allow for monster variance!
int SAFETY_NET = 3;            //Minimum health left after expected monster damage & risk of fumble  - suggested 3
int OOC_HEAL_LEVEL = 80;       //the max HP level at which changing to out of combat healing would be better

// Threshhold for painful combat effects: set to high (only abort on monsters with really bad
// effects), medium (abort on monsters with moderate-to-bad effects), or low (abort on all 
// monsters that could give a bad combat effect)
string EFFECT_THRESHOLD = "medium";

// Set threshhold for MP ... if you are more than MANA_PCT % the script will stop stasising
int MANA_PCT = 90;

// And since the good ol' Bugbear eats MP like it was Pringles ...
if(my_familiar()==$familiar[Pygmy Bugbear Shaman]) MANA_PCT=95;

//if your moxie exceeds monster attack by this much, don't bother trying to stasis (as mob can't hit)
int OVERMOXIE_LEVEL = 8;

#print debug messages.  These go to the Mafia status bar, and the gCLI.  Turn them off if
#you're happy with the performance, and don't care about tweaking/understanding what's going on.
boolean dbug = true;

//Use to show more detailed output
boolean bVerbose = true;

#*** End of user-configurable variables ***#

#Used for status messages
int giRound;

int giCurDelevel;
boolean gbEnemyNoodled;
boolean gbInValley;
stat gDefenceStat;
monster zilch=$monster[];

boolean bUsedNoodles;
string MSG_NOODLEBREAK = "Your opponent struggles free of your entangling noodles.";


int max(int a, int b)
{
	int iRetVal;
 	if (a > b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int min(int a, int b)
{
	int iRetVal;
 	if (a < b)
 	{
		iRetVal = a;
 	}
 	else
 	{
 		iRetVal = b;
 	}
 	return iRetVal;
}

int LeatherBackDR()
{
	//DR from Leatherback, as dirkdiggler's formula:
	//Leatherback DR = Clamp[ 1 < ceil( (level-3)/2 ) ]

	int iResult;
	if (have_skill($skill[skin of the leatherback]))
	{
		iResult = (max(1, ceil( (my_level()-3)/2 )));
	}
	else
	{
		iResult = 0;
	}
	return iResult;
}

void ShowStatusMsg(string Msg)
{
	print( "Round: " + giRound + " " +
	"(MP Change=" + (my_mp()-string_to_int(get_property("IllStasis_StartMP"))) +      				", HP Change=" + (my_hp()-string_to_int(get_property("IllStasis_StartHP"))) +  ") --> " + Msg );
}

boolean ShieldEquipped()
{
	boolean bFoundShield = false;
	string sItem;
	sItem = item_to_string(current_equipment($slot[off-hand]));
	bFoundShield = bFoundShield || (index_of(sItem, "buckler" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "shield" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "hors d'oeuvre tray" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "box turtle" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "coffin lid" )>=0);
	bFoundShield = bFoundShield || (index_of(sItem, "sewer turtle" )>=0);

	if (bVerbose) { Print("Offhand=" + sItem + "-> Shield=" + boolean_to_string(bFoundShield)); }

	return bFoundShield;
}

int CheckSkill(skill myskill)
//return 0=success, 1=insufficient mana, 2=do not have skill
{
    if (!have_skill(myskill))
    {
       return 2;
    }
    else
    {
		if (my_mp() < mp_cost(myskill) )
		{
		   return 1;
		}
	}
	return 0;
}

// max fumble damage if you're "hitting" with a weapon
int get_fumble()
{
	// expected fumble damage for the equipped weapon
	int damage = ceil(get_power(current_equipment($slot[weapon]))/10);         
	if (item_amount($item[goodfella contract])>0 && USE_GOODFELLA && my_familiar()!=$familiar[Penguin Goodfella])
	  //if you're not hitting with a weapon, you won't fumble
		 { damage = 0; }
	if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
	  //if you're not hitting with a weapon, you won't fumble
		 { damage = 0; }
	if (item_amount($item[dictionary])>0 && !gbInValley)
		 { damage = 0; }
	if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
		 { damage = 0; }
	if (have_skill($skill[shake hands]) )
		 { damage = 0; }
	return damage;
}


string StasisSafe(monster eek) {

	if(EFFECT_THRESHOLD=="low") {
	
		// Trigger on low-impact effects, which generally affect a single stat
		if (eek == $monster[7-foot dwarf foreman])
			// Grilled
			return "finish";
		if (eek == $monster[disease-in-the-box])
			// The Disease
			return "finish";
		if (eek == $monster[decent lumberjack])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjack supervisor])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjill])
			// axe wound
			return "finish";
		if (eek == $monster[lumberjuan])
			// axe wound
			return "finish";
		if (eek == $monster[conjoined zmombie])
		{
			// half-eaten brain
                if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		else { return "finish"; }
		}
		if (eek == $monster[corpulent zobmie])
		{
			// half-eaten brain
                  if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
                  else { return "finish"; }
		}
		if (eek == $monster[grave rober zmobie])
		{
			// half-eaten brain
                   if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		   else { return "finish"; }
		}
		if (eek == $monster[zombie waltzers])
		{
			// half-eaten brain
                 if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		 else { return "finish"; }
		}
		if (eek == $monster[zmobie])
		{
			// half-eaten brain
                 if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		 else { return "finish"; }
		}
		if (eek == $monster[big creepy spider])
			// hardly poisoned
			return "finish";
		if (eek == $monster[completely different spider])
			// hardly poisoned
			return "finish";
		if (eek == $monster[mayonnaise wasp])
			// hardly poisoned
			return "finish";
		if (eek == $monster[mind flayer])
			// confused
			return "finish";

	}

	if(EFFECT_THRESHOLD=="medium") {
	
		// Trigger on medium-impact effects: Moderate poisonings, etc.
		if (eek == $monster[acid blob])
			// corroded weapon
			return "sauce";
		if (eek == $monster[whitesnake])
			// a little bit poisoned
			return "finish";
		if (eek == $monster[dodecapede])
			// a little bit poisoned
			return "finish";
		if (eek == $monster[spectral jellyfish])
			// somewhat poisoned
			return "finish";
		if (eek == $monster[swarm of killer bees])
			// somewhat poisoned
			return "finish";

	}

	// Always trigger on high-impact effects

	if (eek == $monster[crusty hippy])
	{
		// socialismydia
                if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		else { return "finishnow"; }
	}
	//if (eek == $monster[procrastination giant])
	//	// cunctatitis
	//	return "cleesh";
	if (eek == $monster[protagonist])
		// temporary amnesia
		return "finishnow";
	if (eek == $monster[quantum mechanic])
		// teleportitis
		return "finishnow";
	if (eek == $monster[empty suit of armor])
		// tetanus
		{
                if(have_skill($skill[immaculate seasoning]) &&  CheckSkill($skill[stream of sauce])==0){return "sauce"; }
		else { return "finishnow"; }
		}

	// If we got here, no worries!  Stasis away, you filthy stasis monkey.
	
	return "safe";

}


skill Choose_spell(skill WhichSpell, int my_myst, int hp_target)
{

	// Spell bonuses - sauce only, no check for cookbook or flavour of magic as these are unlikely!
	int spicebonus=0;
	int expectdam=0;
	if(have_skill($skill[intrinsic spiciness])) spicebonus=min(my_level(),10);
	if(have_skill($skill[immaculate seasoning])) spicebonus=spicebonus+10;

	
	record spells { skill name; int spelldam; } ;
	spells [int] spellsling;
	spellsling [1].name = $skill[saucegeyser];		// 40 mp
	spellsling [1].spelldam = (my_myst*0.35)+35 + spicebonus;
	spellsling [2].name = $skill[weapon of the pastalord]; 	// 35 mp
	spellsling [2].spelldam = (my_myst*.35)+32;	
	spellsling [3].name = $skill[wave of sauce];		// 23 mp
	spellsling [3].spelldam = min((my_myst*0.3)+20,50) + spicebonus;
	spellsling [4].name = $skill[cone of whatever];		// 19 mp
	spellsling [4].spelldam = min((my_myst*.25)+16,46);
	spellsling [5].name = $skill[saucestorm];		// 12 mp
	spellsling [5].spelldam = min((my_myst/5)+14,39) + spicebonus;
	spellsling [6].name = $skill[extreme ray of something];	// 7 mp
	spellsling [6].spelldam = min((my_myst*.15)+8,28);
	spellsling [7].name = $skill[minor ray of something];	// 4 mp
	spellsling [7].spelldam = min((my_myst*.07)+3,18);
	spellsling [8].name = $skill[stream of sauce];		// 3 mp
	spellsling [8].spelldam = min((my_myst/10)+3,18) + spicebonus;

	skill possible;
	int possible_dam;

	foreach key in spellsling
	{
	possible = spellsling[key].name;
	possible_dam = spellsling[key].spelldam;

	if(checkSkill(possible)==0)
		{
		if(possible_dam>=hp_target)
			{
			WhichSpell=possible;
			expectdam = possible_dam;
   		        if(bVerbose) print(skill_to_string(WhichSpell)+" should do "+int_to_string(expectdam)+" damage.");
			}
		}
	}
	return WhichSpell;

}

void SauceKill(monster eek) {

	// Guess at monster remaining HP, see which spell we have that can do as close 
	// to a one-hit kill as possible
	
	int my_myst=my_buffedstat($stat[mysticality]);
	int hp_target=monster_base_hp(eek)+monster_level_adjustment();
	if(dbug) print("SauceKill: need to do " + hp_target + " points of damage in one turn.");

	
	skill whichSpell=$skill[none];

	Whichspell = Choose_spell(WhichSpell, my_myst, hp_target);
	
	if(WhichSpell=$skill[none])	//no 1-hit kill, check for 2-hit
		{
		hp_target=hp_target/2;
		Whichspell = Choose_spell(WhichSpell, my_myst, hp_target);		
		}

	//If a 1-hit isn't possible, it's worth casting noodles - but only if we've at least 3 rounds to play with
	if(CheckSkill($skill[entangling noodles])==0 && bUsedNoodles==false && giRound<27)
	{
		use_skill($skill[entangling noodles]);
		set_property("IllNeoStasis_UsedNoodles", "true");
		set_property("IllNeoStasis_EnemyNoodled", "true");
	}


	//Or, if we've selected a spell
	if(WhichSpell !=$skill[none])
	{
		if(dbug) print("Casting "+skill_to_string(WhichSpell));
		use_skill(WhichSpell);
	}
	else
	{
		if(CheckSkill($skill[stream of sauce])==0 && giRound<=26)	//allowing time to kill, hopefully!
		{
			if(dbug) print("Casting stream of sauce ... which is better than nothing.");
			use_skill($skill[stream of sauce]);
		}
		else
		{
			// Hmm.  Got CLEESH?
			if(CheckSkill($skill[cleesh])==0) whichSpell=$skill[cleesh];
			else attack();
		}
	}	
}



boolean CheckPhysicalResistance(monster eek)
{
	if (eek == $monster[chalkdust wraith])
		return true;
	if (eek == $monster[Ghost Miner])
		return true;
	if (eek == $monster[Snow Queen])
		return true;
	return false;
}

int ExpectedMonsterDamage(monster eek)
//BaseMonsterDmg = (20% to 25%)*Atk + (Atk-Mox)
//Dam = (Base - DR) * DA * ER
{
 	 int iDam;
 	 int iAtk;
 	 int iMoxPenalty;
 	 int iDR;
	 iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;

	 iMoxPenalty = max(iAtk - my_buffedstat(gDefenceStat),0);
	 iDam = 0.25*iAtk + iMoxPenalty;
	 iDam = iDam - damage_reduction()-LeatherbackDR();
	 iDam = iDam - (iDam * damage_absorption_percent()/100);

	 //TODO elemental resistance

	 // if (bVerbose) { Print("Expected:  (25% of " + iAtk + "+" + iMoxPenalty + "-(" + damage_reduction() + "+" + LeatherBackDR() + ")DR) * " + damage_absorption_percent() + "%DA = " + iDam); }

	 return iDam;
}

boolean MonsterCannotHit(monster eek)
//Test if mob can hit us
{
	int iAtk;
	iAtk = monster_base_attack(eek)+ monster_level_adjustment() - giCurDelevel;
	return ((iAtk + OVERMOXIE_LEVEL) <= my_buffedstat($stat[moxie]) );
}

boolean PlayerCannotHit(monster eek)
//Test if we can hit the mob
{
	int iDef;
	iDef = monster_base_defense(eek)+ monster_level_adjustment() - giCurDelevel;
	return (my_buffedstat(current_hit_stat()) < (iDef + 7)); //TODO check the 7
}

void PickPocket()
{
	print("Picking pocket...");
	visit_url("fight.php?action=steal");

}

boolean ThrowJunkItem()
{
	item JunkItem;
	JunkItem = $item[scroll of turtle summoning];
	if (bVerbose) { Print("Trying to throw junk..."); }

	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[seal tooth];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}
	JunkItem = $item[spices];
	if (item_amount(JunkItem)>0)
	{
		throw_item(JunkItem);
		return true;
	}

	if (bVerbose) { Print("Failed to throw junk."); }
	return false;
}


void FinishHim(monster eek)
{
	if (dbug) { print("Finish Him!"); }
	if (gbInValley)
	//Best to use a dictionary if we can - free
	{
		if (item_amount($item[dictionary])>0)
		{
			if (bVerbose) { Print("Finish with dictionary"); }
			throw_item($item[dictionary]);
		}
		else
		{
			if (item_amount($item[facsimile dictionary])>0)
			{
				if (bVerbose) { Print("Finish with dictionary"); }
				throw_item($item[facsimile dictionary]);
			}
		}
	}
	else
	{
		if (CheckPhysicalResistance(eek) && have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
		{
			if (bVerbose) { Print("Physical Resistant - Finish with Stream"); }
			use_skill($skill[stream of sauce]);
		}
		else
		{
			#check for melee weapon
			if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[thrust-smack])==0 && eek!= $monster[procrastination giant] && !gbEnemyNoodled)
			{
				if (bVerbose) { Print("Finish with TS"); }
				use_skill($skill[thrust-smack]);
			}
			else
			{
				if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 )
				{
					if (bVerbose) { Print("Finish with Stream"); }
					use_skill($skill[stream of sauce]);
				}
				else
				{
					if (current_hit_stat() == $stat[muscle] && CheckSkill($skill[lunging thrust-smack])==0 && eek!= $monster[procrastination giant])
					{
						if (bVerbose) { Print("Finish with LTS"); }
						use_skill($skill[lunging thrust-smack]);
					}
					else
					{
						if (!ThrowJunkItem())
						{
							if (dbug) { print("Aborting - you must finish.  Two rounds left!"); }
							cli_execute("abort");
						}
					}
				}
			}
		}
	}
}

void FinishHimNow(monster eek)
//Finish ASAP, but using stream if enemy is noodled, and LTS in preference to TS
{

	int damageTarg = monster_base_hp(eek)+monster_level_adjustment();

	#check for melee weapon
	if (current_hit_stat() == $stat[muscle] && !CheckPhysicalResistance(eek) && eek != $monster[procrastination giant] && !gbEnemyNoodled ) {
		if(CheckSkill($skill[lunging thrust-smack])==0 && damageTarg>35) {

			// First preference: finish with LTS
			if (bVerbose) { Print("Finish with LTS"); }
			use_skill($skill[lunging thrust-smack]);
		} else {
			if(CheckSkill($skill[thrust-smack])==0) {
				// Second preference: finish with TS
				if (bVerbose) { Print("Finish with TS"); }
				use_skill($skill[thrust-smack]);
			}
		}
	}	
	else
	{
		SauceKill(eek);
		// Either we are not holding a melee weapon, or the monster is phys resistant.
		// Spell time!
		// First preference: finish with Saucestorm
		//if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[saucestorm])==0 ) {
		//	if (bVerbose) { Print("Finish with Storm"); }
		//	use_skill($skill[saucestorm]);
		//} else {
		//
		//	if (have_skill($skill[immaculate seasoning]) && CheckSkill($skill[stream of sauce])==0 ) {
		//		if (bVerbose) { Print("Finish with Stream"); }
		//		use_skill($skill[stream of sauce]);
		//	} else {
		//		if (!ThrowJunkItem()) {
		//			if (dbug) { print("Aborting - you must finish!"); }
		//			cli_execute("abort");
		//		}
		//	}
		//}
	}

}

void DontHurtHim(monster eek)
{
	if (bVerbose) { Print("Attack and miss"); }

	if (item_amount($item[anti-anti-antidote])>0 && USE_ANTIDOTE)
	{
	   throw_item($item[anti-anti-antidote]);
	}
	else
	{
		if (item_amount($item[goodfella contract])>0 && USE_GOODFELLA && my_familiar()!=$familiar[Penguin Goodfella])
		{
		   throw_item($item[goodfella contract]);
		}

		else
		{
			if (item_amount($item[dictionary])>0 && !gbInValley)
			{
				throw_item($item[dictionary]);
			}
			else
			{
				if (item_amount($item[facsimile dictionary])>0 && !gbInValley)
				{
					throw_item($item[facsimile dictionary]);
				}
				else
				{
					if (have_skill($skill[shake hands]) )
					{
					use_skill($skill[shake hands]);
					}
					else
					{
						if (buffed_hit_stat() - (monster_base_attack(eek)+ monster_level_adjustment()
							- giCurDelevel) < -5)
						{
							#attack and miss
							attack();
						}
						else
						//nothing we can do except throw junk, or attack and hit
						{
							if( eek !=$monster[procrastination giant] )
								{ Saucekill(eek); }
							else {
								if (!ThrowJunkItem())
								 {	attack(); }
							}					

						}
					}
				}
			}
		}
	}
}


void main(int iRound, monster eek, string sText)
{
	// if (dbug) { print("Starting NeoStasis"); }

	int iDamTaken=0;
	string sTemp;
	string sName;
	int iStart=0;
	int iEnd=0;


	string sAction;

	// Define correct defense stat
	if (ShieldEquipped() && have_skill($skill[hero of the half-shell]) &&
		( my_buffedstat($stat[muscle]) > my_buffedstat($stat[moxie]) ) )
	{
		gDefenceStat = $stat[muscle];
	    set_property("IllNeoStasis_Hero", "true");
		// if (dbug) { print("Hero!"); }
	}
	else
	{
		gDefenceStat = $stat[moxie];
	    set_property("IllNeoStasis_Hero", "false");
		// if (dbug) { print("Zero :("); }
	}


	//Sets appropriate health levels
	// Decide the expected monster damage - first 4 rounds based on calculation, 5th and on based on damage taken this fight 
	// Based on DarthDud's suggestions, added by BDrag0n
	int ExpectedDamage=ExpectedMonsterDamage(eek);
	if(iRound >= 5)
	{
		ExpectedDamage = string_to_int(get_property("IllNeoStasis_MaxDamTaken"));
                if(dbug) { Print ("Expected damage based on this fight is: "+int_to_string(ExpectedDamage)); }
	}	
	
	int MIN_HEAL_THRESHOLD = ExpectedDamage+get_fumble() + SAFETY_NET;  //lowest level to heal at

	int HEAL_THRESHOLD;
	if(my_maxhp()<OOC_HEAL_LEVEL)    {
		HEAL_THRESHOLD = my_maxhp()-15;
		if(have_effect($effect[purple tongue]) > 0)        // allow for purple tongue healing
			{ HEAL_THRESHOLD = HEAL_THRESHOLD-20; }
		if(have_effect($effect[Heart of Orange]) > 0)  // Allow for orange heart healing
			{ HEAL_THRESHOLD = HEAL_THRESHOLD-6; }
		if(HEAL_THRESHOLD < MIN_HEAL_THRESHOLD)      // don't want to drop below the minimum health!
			{ HEAL_THRESHOLD = MIN_HEAL_THRESHOLD; }
	} else {
		HEAL_THRESHOLD = MIN_HEAL_THRESHOLD;      // So if your max health is above the OOC_HEAL_THRESHOLD you want to allow minimal health at the end of battle
	}

	// if(dbug) print("Your heal threshhold is " + HEAL_THRESHOLD);



	// Start the round!
	giRound = iRound;
	print ("++++++++++++++++++++++++++++++++");
        print("Round " + iRound + " vs " + eek);
	#print(sText);
	print ("++++++++++++++++++++++++++++++++");

	gbInValley = (my_location() == $location[valley beyond orc chasm]);
	if (dbug) { print("Valley=" + boolean_to_string(gbInValley)); }
	int MaxDamTaken;

	if (iRound == 1)
	{
		giCurDelevel = 0;
		set_property("IllStasis_Delevel", "0");
		set_property("IllStasis_StartMP", my_mp());
		set_property("IllStasis_StartHP", my_hp());
		bUsedNoodles = false;
		set_property("IllNeoStasis_UsedNoodles", "false");
		gbEnemyNoodled = false;
		set_property("IllNeoStasis_EnemyNoodled", "false");
		set_property("IllNeoStasis_MaxDamTaken", "0");
	}
	else
	{
		giCurDelevel = string_to_int(get_property("IllStasis_Delevel"));
		//if (dbug) { print("giCurDelevel=" + giCurDelevel); }
		bUsedNoodles = string_to_boolean(get_property("IllNeoStasis_UsedNoodles"));
		gbEnemyNoodled = string_to_boolean(get_property("IllNeoStasis_EnemyNoodled"));
		MaxDamTaken=string_to_int(get_property("IllNeoStasis_MaxDamTaken"));

	}


	// Check damage taken

	iStart = index_of(sText, "You lose " );
	if (iStart > 0)
	{
		iEnd = index_of(sText, " hit point", iStart );
	}
	// if (dbug) { print("iStart = " + iStart + "iEnd = " + iEnd); }
	if (iStart > 0 && iEnd > 0)
	{
		sTemp = substring(sText,iStart+9,iEnd );
		iDamTaken = string_to_int(sTemp);
		if (dbug) { print("Took Dam:  " + iDamTaken); }
		if(iDamTaken > MaxDamTaken) { set_property("IllNeoStasis_MaxDamTaken", int_to_string(iDamTaken)); }
	}

	// Check Noodles status
	
	iStart = 0;
	iStart = index_of(sText, MSG_NOODLEBREAK);
	if (iStart > 0)
	{
		gbEnemyNoodled = false;
		set_property("IllNeoStasis_EnemyNoodled", "false");
	}

	// Report on current HP and MP threshholds

	int iHPPct;
	iHPPct = 100*my_hp()/my_maxhp();
	int iMPPct = 100*my_mp()/my_maxmp();
	if (dbug) { print("HP = " + iHPPct + "%  (HP=" + my_hp() + "/" + my_maxhp() + ")"); }
	if (dbug) { print("MP = " + iMPPct + "%  (MP=" + my_mp() + "/" + my_maxmp() + ")"); }

	//Decide on our action, in reverse priority order

	sAction = "Miss"; //Default

	if (MonsterCannotHit(eek))
	{
		if (dbug) { print("Monster cannot hit..."); }

		if (PlayerCannotHit(eek))
		{
			if (dbug) { print("We cannot hit either...finish"); }
			sAction = "Finish";
		}
		else
		{
			sAction = "Attack";
		}
	}

	// Are we full on MP?  If so, we don't need to draw this out.

	if (iMPPct  >= MANA_PCT)
	{
		//
		if (dbug) { print("We're full on MP!"); }
		if (iHPPct<85 && CheckSkill($skill[saucy salve]) == 0)
		{
			// If we are below 85% HP, might as well burn some MP on healing
			sAction = "Salve";
		}
		else
		{
			sAction = "Finish";
		}
	}


	if (my_hp() < HEAL_THRESHOLD)
	{
		//We're hurting, heal if no higher priority action
		if (CheckSkill($skill[saucy salve]) == 0)
		{
			sAction = "Salve";
		}
	}

	if (giRound == 1 && my_primestat()==$stat[moxie] && index_of(sText, "You get the jump") >= 0)
	{
		sAction = "Pickpocket";
	}

	if (iDamTaken >= DAM_THRESHOLD)
	//TODO
	//Too painful!  End fight quickly
	{
		sAction = "FinishNow";
	}
	//else
	//{

	//}

	// Check to see if the monster is safe

	string safetest=StasisSafe(eek);
	if(safetest!="safe") {
		if (dbug) { print("Monster is not safe!  Result=" + safetest); }
		if(safetest=="sauce") {
			sAction="SauceKill";
		} else {
			if (safetest=="cleesh") {
				sAction="cleesh";
			} else {
				if(safetest=="finishnow") {
					sAction="FinishNOW";
				} else sAction="Finish";
			}
		}
	}


	if (ExpectedDamage > (my_hp() + SAFETY_NET) && (my_maxHP()-my_HP() )> 10)
	//danger of death - panic
	//TODO
	{
		if(my_maxhp()-my_hp()<=10) {
			sAction = "Salve";
		} else {
			if (dbug) { print("PANIC! : Noodled=" + gbEnemyNoodled + ", UsedNoodles=" + bUsedNoodles); }

			if (gbEnemyNoodled)
			{
				if (my_hp()+10>HEAL_THRESHOLD)
				{
					//heal back to safety level
					if (CheckSkill($skill[saucy salve]) == 0)
					{
						sAction = "Salve";
					}
				}
				else
				{
					sAction = "FinishNOW";
				}
			}
			else
			{
				if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
				{
					sAction = "Noodle";
				}
				else
				{
					sAction = "FinishNOW";
				}
			}
		}
	}


	if (iRound >= 29)
	{
	   #long enough!
		sAction = "Finish";
	}

	ShowStatusMsg("Action = " + sAction);

	//And finally, perform our action
	if (sAction == "Attack") attack();
	if (sAction == "FinishNOW") FinishHimNOW(eek);
	if (sAction == "Finish") FinishHim(eek);
	if (sAction == "Salve") use_skill($skill[saucy salve]);
	if (sAction == "Cleesh") {
		if(CheckSkill($skill[cleesh]) == 0) use_skill($skill[cleesh]);
			else sAction="SauceKill";
	}
	if (sAction == "Miss") DontHurtHim(eek);
	if (sAction == "Pickpocket") Pickpocket();
	if (sAction == "SauceKill") SauceKill(eek);
	if (sAction == "Noodle")
	{
		use_skill($skill[entangling noodles]);
		set_property("IllNeoStasis_UsedNoodles", "true");
		set_property("IllNeoStasis_EnemyNoodled", "true");
	}
}
 

Attachments

  • NewAshStasis.ash
    25.9 KB · Views: 92

DarthDud

New member
#Monster variance
We can use the current damage predictor in order to predict the high-end of possible variance, could we not?

We could continue to use this method, which already overestimates damage, for the first 5 rounds to be safe.
Code:
iDam = 0.25*iAtk + iMoxPenalty;
iDam = iDam - damage_reduction()-LeatherbackDR();
iDam = iDam - (iDam * damage_absorption_percent()/100);
Then, once we switch over to the new damage predictor (based on damage received so far), use this equation to calculate maximum possible damage, factoring in variance, so the script knows if we are falling to dangerous health levels or whatnot more precisely. This would be something like this:
Code:
iDamMax= iDam - iDamTaken;
This will generally overestimate how much more damage the monsters can deal than we predict, since iDamTaken will already be on the higher end of monster variance most of the time. However, a couple HP worth of safety net might be desirable.

But I'm not much of a coder, so tell me if I'm being stupid. :)
 

BDrag0n

Member
Actually, that's an oversight on my part :-[ - those "TO DO" options are already dealt with by the changes made by Gemelli and your damage tracking suggestion:

Abort on specific monsters - is dealt with in the "stasis safe" monsters section.

Monster variance is only important for the damage being taken, which is already factored in for the minheal and panic calculations based on the damage tracking already implemented. The code you have there would show the variance from the calculated damage - but unless I'm missing something it wouldn't add to the functionality of the script. If I *am* missing something I'm more than happy to add it!

In the meantime, I'll remove the 2 to-do items from the list ;D
 

DarthDud

New member
Does not appear to know the new stats for the palindome monsters... keeps TSing them since it thinks they can't hit me. :(
 

Veracity

Developer
Staff member
[quote author=DarthDud link=topic=799.msg4252#msg4252 date=1177096222]
Does not appear to know the new stats for the palindome monsters... keeps TSing them since it thinks they can't hit me. :([/quote]

Here's what Kittiwake's Monster Survival Guide says about those monsters:

The Palindome - ?? Hit - ?? Evade
Bob Racecar - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Drab Bard - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Evil Olive - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Flock of Stab-Bats - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Racecar Bob - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Taco Cat - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade
Tan Gnat - ?? HP - ??XP - ?? Def - ?? Atk - ?? Hit - ?? Evade

Got any better numbers? ;)
 

muffins

Member
At first glance, I can't see where it would be best to insert "Temporary Amnesia" protection... can anyone make any suggestions so that, if I have this effect, it will only use physical attacks instead of skills? Thanks in advance! :-*
 

BDrag0n

Member
[quote author=muffins link=topic=799.msg4310#msg4310 date=1177480363]
At first glance, I can't see where it would be best to insert "Temporary Amnesia" protection... can anyone make any suggestions so that, if I have this effect, it will only use physical attacks instead of skills? Thanks in advance!  :-*
[/quote]

Protection is already there - the Protagonist will get clobbered as fast as possible.

If you still *get* the effect, then pretty much everything in the script is useless for the rest of that fight. So what I'd do is
Code:
 void main()
{
 if(have_effect($effect[temporary amnesia])>0)
 { cli_execute("abort: You've forgotten how to do this!"); }

That will pop up a mini-browser for you to finish the fight, and deal with the effect yourself (rather than risk it running another fight with the effect still active, for example if you don't have any SGEEAs).


On another note:

Since Mafia now parses damage done to monsters (and can output this to the session log) - is it possible to use this information in a consult script? Or if not, could it be? Pretty please? ;)
 

holatuwol

Developer
Please provide function name, since I don't know if you want how much health the monster has remaining (on average), or if you're looking to see how much damage has been done so far.  Please make sure your function name is as expressive as possible.

Also, provide any other functions pertaining to monster-related information you're interested in knowing that you believe KoLmafia is tracking if you'd like those functions as well. Currently, KoLmafia tracks which monster it thinks you're fighting (obviously) and it tracks how much that monster has been deleveled. So, it can return almost anything derived from those values via a simple formula, provided those simple formulas are documented in the request.
 

illarion

Member
Please provide function name, since I don't know if you want how much health the monster has remaining (on average), or if you're looking to see how much damage has been done so far. Please make sure your function name is as expressive as possible.

Remaining monster health would be the most useful, though damage done certainly wouldn't hurt either.
I'd suggest
int monster_hp_left( monster eek ) or
int monster_hp_remaining( monster eek )

and
int monster_dam_taken( monster eek ) or
int monster_damage_taken( monster eek )

(to parallel the existing
int monster_base_attack( monster eek )
int monster_base_defense( monster eek )
int monster_base_hp( monster eek )
functions)

Also, provide any other functions pertaining to monster-related information you're interested in knowing that you believe KoLmafia is tracking if you'd like those functions as well. Currently, KoLmafia tracks which monster it thinks you're fighting (obviously) and it tracks how much that monster has been deleveled. So, it can return almost anything derived from those values via a simple formula, provided those simple formulas are documented in the request.

monster_buffed_attack (monster_base_attack + monster_level_adjustment - ?monster_delevel?)
monster_buffed_defense (monster_base_defense + monster_level_adjustment - ?monster_delevel?)
monster_buffed_hp (monster_base_hp + monster_level_adjustment - ?monster_delevel?)
would all be useful.

Being able to access a monster's elemental attack type and probability would be awesome, though possibly lots more work (Mafia must have this info though, for the location details pane?).

That's all I can think of for now - BDragon?
 

illarion

Member
Well, I wouldn't especially - but that's the format all the other (admittedly non-combat also) functions I use in my script use. Seemed tidy and consistent, though admittedly redundant.

I'll leave that one up to you ;)

I was editing it as you replied, I'll put the new bit here instead so it can't get missed:

Oh, a pair of functions:
boolean monster_can_hit
boolean player_can_hit

Which would return a boolean indicating a non-zero chance of hitting with a standard attack with the current weapon, delevel etc (ignoring criticals). Or they could return the % chance, that would probably be more generally useful.

I can't find the up to date hit% formula now, irritatingly, though I know it's on the HCO forums somewhere...
 
Top