View Full Version : Illarion's NeoStasis script v1.4
illarion
02-28-2007, 02:18 PM
# 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.[hr]
# 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]);
}
}
illarion
03-16-2007, 12:57 AM
bump for v1.2
illarion
03-21-2007, 03:15 PM
bump for v1.3
illarion
03-28-2007, 10:33 PM
bump for v1.4
BDrag0n
03-30-2007, 11:21 PM
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.
# 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
03-31-2007, 01:09 AM
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
04-09-2007, 03:01 PM
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 :)
BDrag0n
04-13-2007, 12:27 PM
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 :-)
# 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_adjus tment();
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
04-13-2007, 09:14 PM
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:
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.
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:
HEAL_THRESHOLD = MIN_HEAL_THRESHOLD + 10; // Added extra buffer to prevent false panic
// 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:
// 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
}
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.
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.
BDrag0n
04-14-2007, 05:25 PM
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
if (ExpectedDamage > (my_hp() + 10))
//danger of death - panic to
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.[hr]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.
# *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_adjus tment();
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");
}
}
DarthDud
04-16-2007, 03:26 PM
#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.
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:
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
04-16-2007, 05:07 PM
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
04-20-2007, 07:10 PM
Does not appear to know the new stats for the palindome monsters... keeps TSing them since it thinks they can't hit me. :(
Veracity
04-20-2007, 08:10 PM
Does not appear to know the new stats for the palindome monsters... keeps TSing them since it thinks they can't hit me. :(
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
04-25-2007, 05:52 AM
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
04-25-2007, 11:24 AM
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! *:-*
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
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
04-25-2007, 01:15 PM
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
04-25-2007, 01:40 PM
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?
holatuwol
04-25-2007, 01:43 PM
... Just out of curiosity, why would you want to be able to specify the monster in those function calls?
illarion
04-25-2007, 01:53 PM
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...
holatuwol
04-26-2007, 01:05 AM
element monster_attack_element( monster ): Added
element monster_defense_element( monster ): Added
element monster_attack_element(): Added
element monster_defense_element(): Added
int monster_attack(): Added
int monster_defense(): Added
int monster_hp(): Added
boolean will_usually_miss(): Added
boolean will_usually_dodge(): Added
efilnikufecin
04-26-2007, 03:03 AM
element monster_attack_element( monster ): Added
element monster_defense_element( monster ): Added
element monster_attack_element(): Added
element monster_defense_element(): Added
int monster_attack(): Added
int monster_defense(): Added
int monster_hp(): Added
boolean will_usually_miss(): Added
boolean will_usually_dodge(): Added
And they have been added to the scripting wiki also, I hope my descriptions are correct.
gemelli
04-26-2007, 04:37 AM
The response you guys provide via this forum is nothing short of phenomenal. Kudos!
As long as we're adding a check for Amnesia:
if(have_effect($effect[Jabaņero Saucesphere])==0)
{ cli_execute("abort: Oops! You forgot the sauce."); }
On that note, it might be best to add a general effect-checking function that tweaks the combat handling logic in a few places ... e.g., if we're poisoned, we may not want to use the antidote action unless we have more than one antidote on hand. Although I'm not planning to make any major mods to the script myself until all those juicy new functions are available in a public release. You can't see it from here, but I am salivating :D
illarion
04-26-2007, 01:02 PM
Thanks Hola, very much appreciated!
One question:
boolean will_usually_miss(): Added
boolean will_usually_dodge(): Added
What exactly does "usually" mean in this context? "Always unless there is a critical", "5%", etc?
Gemelli> Agree with the idea.
Now if only we could queue non-combat skill use after the combat... ;)
holatuwol
04-26-2007, 04:14 PM
>= 80% in the average case. It's the same tests KoLmafia uses internally to decide when to stop deleveling or when it's okay to steal.
BDrag0n
04-28-2007, 04:20 PM
OK, thanks to the wonderful new changes, and a few requests / ideas - here is a new version.
Note that this is NOT FULLY TESTED and is being posted so Illarion or Gemelli don't waste hours reproducing the same w**k - instead they can pick my hopeless coding to pieces and improve on it!
So - not fully tested, don't rely on it yet, etc etc - but here's what I've been w**king on. It is substantially changed from before, most of the changes are detailed at the top in the changelog but you can see that I've made quite a few changes in the coding.
BDrag0n
# NeoStasis.ash
# v2
# by Illarion, BDrag0n & 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
# v2 (28/04/07) Major changes by BDrag0n, mostly enabled by new functions added by Holatuwol
# Armour moved to Medium unsafe stasis list, and high if tetanus would take you below 68 mainstat.
# Aborts added for Amnesia, or no jab sphere running.
# Added elemental defence to expected monster damage
# Will not use up your last anti-anti-antidote during combat.
# Will try to kill immediately if expected damage is too high
# Changed killing logic
# - All spells and TS/LTS are considered at the same time - with exceptions for Procrastination & physical resistance.
# - takes into account elemental damage & weaknesses (note: not for pastamancer spells)
# - does cheapest attack which will kill in 1-shot where possible.
# - When kill logic fails to find a skill to use, abort rather than try and hit
# - will always try to kill as fast as possible where killing is necessary
# 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_MAX the script will stop stasising
int MANA_MAX = my_maxMP()-10;
// And since the good ol' Bugbear eats MP like it was Pringles ...
if(my_familiar()==$familiar[Pygmy Bugbear Shaman]) MANA_MAX=my_maxMP();
//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]) return "finish"; // Grilled
if (eek == $monster[disease-in-the-box]) return "finish"; // The Disease
if (eek == $monster[decent lumberjack]) return "finish"; // axe wound
if (eek == $monster[lumberjack supervisor]) return "finish"; // axe wound
if (eek == $monster[lumberjill]) return "finish"; // axe wound
if (eek == $monster[lumberjuan]) return "finish"; // axe wound
if (eek == $monster[conjoined zmombie]) return "finish"; // half-eaten brain
if (eek == $monster[corpulent zobmie]) return "finish"; // half-eaten brain
if (eek == $monster[grave rober zmobie]) return "finish"; // half-eaten brain
if (eek == $monster[zombie waltzers]) return "finish"; // half-eaten brain
if (eek == $monster[zmobie]) return "finish"; // half-eaten brain
if (eek == $monster[big creepy spider]) return "finish"; // hardly poisoned
if (eek == $monster[completely different spider]) return "finish"; // hardly poisoned
if (eek == $monster[mayonnaise wasp]) return "finish"; // hardly poisoned
if (eek == $monster[mind flayer]) return "finish"; // confused
}
if(EFFECT_THRESHOLD=="medium")
{
// Trigger on medium-impact effects: Moderate poisonings, etc.
if (eek == $monster[acid blob]) return "finish"; // corroded weapon
if (eek == $monster[whitesnake]) return "finish"; // a little bit poisoned
if (eek == $monster[dodecapede]) return "finish"; // a little bit poisoned
if (eek == $monster[spectral jellyfish]) return "finish"; // somewhat poisoned
if (eek == $monster[swarm of killer bees]) return "finish"; // somewhat poisoned
if (eek == $monster[empty suit of armor]) return "finishnow"; // tetanus
}
// Always trigger on high-impact effects - Note cunctatitis is dealt with elsewhere
if (eek == $monster[crusty hippy]) return "finishnow"; // socialismydia
if (eek == $monster[protagonist]) return "finishnow"; // temporary amnesia
if (eek == $monster[quantum mechanic]) return "finishnow"; // teleportitis
if (eek == $monster[empty suit of armor]
&& my_buffedstat(my_primestat())<76) return "finishnow"; // tetanus
// If we got here, no worries! Stasis away, you filthy stasis monkey.
return "safe";
}
int get_spell_bonus(string Spell_class, )
{
int spellbonus=0;
//start with skills
if(Spell_class="sauce")
{
if(have_skill($skill[intrinsic spiciness])) spellbonus=min(my_level(),10);
if(have_skill($skill[immaculate seasoning])) spellbonus=spellbonus+10;
}
// now active effects
if(have_effect($effect[Aspect of the Twinklefairy])>0) spellbonus=spellbonus+5;
if(have_effect($effect[Jackasses' Symphony of Destruction])>0) spellbonus=spellbonus+12;
// Well, that was short. Equipment should have more bonus possibility
if(have_equipped($item[plexiglass pinky ring])) spellbonus=spellbonus+30 ;
if(have_equipped($item[stainless steel scarf])) spellbonus=spellbonus+20 ;
if(have_equipped($item[kickback cookbook])) spellbonus=spellbonus+20 ;
if(have_equipped($item[rib of the bonerdagon])) spellbonus=spellbonus+15 ;
if(have_equipped($item[star spatula])) spellbonus=spellbonus+15 ;
if(have_equipped($item[silver shrimp fork])) spellbonus=spellbonus+14 ;
if(have_equipped($item[oversized pizza cutter])) spellbonus=spellbonus+13 ;
if(have_equipped($item[enchanted toothpick])) spellbonus=spellbonus+12 ;
if(have_equipped($item[chopsticks])) spellbonus=spellbonus+12 ;
if(have_equipped($item[baconstone pendant])) spellbonus=spellbonus+10 ;
if(have_equipped($item[clockwork staff])) spellbonus=spellbonus+10 ;
if(have_equipped($item[huge spoon])) spellbonus=spellbonus+10 ;
if(have_equipped($item[iron pasta spoon])) spellbonus=spellbonus+10 ;
if(have_equipped($item[knob goblin melon baller])) spellbonus=spellbonus+8 ;
if(have_equipped($item[filthy pestle])) spellbonus=spellbonus+7 ;
if(have_equipped($item[hairy staff])) spellbonus=spellbonus+7 ;
if(have_equipped($item[linoleum crossbow])) spellbonus=spellbonus+7 ;
if(have_equipped($item[linoleum sword])) spellbonus=spellbonus+7 ;
if(have_equipped($item[linoleum staff])) spellbonus=spellbonus+7 ;
if(have_equipped($item[projectile icemaker])) spellbonus=spellbonus+7 ;
if(have_equipped($item[shuddersword])) spellbonus=spellbonus+7 ;
if(have_equipped($item[9-ball])) spellbonus=spellbonus+5 ;
if(have_equipped($item[jack flapper])) spellbonus=spellbonus+5 ;
if(have_equipped($item[ring of increase damage])) spellbonus=spellbonus+5 ;
if(have_equipped($item[shiny butcherknife])) spellbonus=spellbonus+5 ;
if(have_equipped($item[knob goblin spatula])) spellbonus=spellbonus+4 ;
if(have_equipped($item[basic meat foon])) spellbonus=spellbonus+3 ;
if(have_equipped($item[bugbear beanie])&& have_equipped($item[bugbear bungguard])) spellbonus=spellbonus+3 ;
if(have_equipped($item[clown wig])) spellbonus=spellbonus+3 ;
if(have_equipped($item[gnollish pie server])) spellbonus=spellbonus+3 ;
if(have_equipped($item[gnollish slotted spoon])) spellbonus=spellbonus+3 ;
if(have_equipped($item[portable corkscrew])) spellbonus=spellbonus+3 ;
if(have_equipped($item[eggbeater])) spellbonus=spellbonus+2 ;
if(have_equipped($item[knob goblin tongs])) spellbonus=spellbonus+2 ;
if(have_equipped($item[corn holder])) spellbonus=spellbonus+1 ;
if(have_equipped($item[little paper umbrella])) spellbonus=spellbonus+1 ;
//I'm going to ignore the concentration potions
return spellbonus;
}
int get_melee_bonus()
{
int add_melee=0;
//start with skills
if(have_skill($skill[claws of the walrus])) add_melee=add_melee+4;
if(have_skill($skill[claws of the otter])) add_melee=add_melee+3;
// now active effects
if(have_effect($effect[Twinkly Weapon])>0) add_melee=add_melee+3;
if(have_effect($effect[Sharp Weapon])>0) add_melee=add_melee+5;
if(have_effect($effect[Pronounced Potency])>0) add_melee=add_melee+5;
if(have_effect($effect[Tenacity of the Snapper])>0) add_melee=add_melee+8;
if(have_effect($effect[Rage of the Reindeer])>0) add_melee=add_melee+10;
if(have_effect($effect[PaPowerful])>0) add_melee=add_melee+10;
if(have_effect($effect[Engorged Weapon])>0) add_melee=add_melee+10;
if(have_effect($effect[Aspect of the Twinklefairy])>0) add_melee=add_melee+5;
if(have_effect($effect[Jackasses' Symphony of Destruction])>0) add_melee=add_melee+12;
if(have_effect($effect[Ponderous Potency])>0) add_melee=add_melee+20;
// and equipment - note that this will not spot duplicate equipment
if(have_equipped($item[flaming talons])) add_melee=add_melee+5 ;
if(have_equipped($item[plexiglass pike pike])) add_melee=add_melee+25 ;
if(have_equipped($item[stainless steel shillelagh])) add_melee=add_melee+20 ;
if(have_equipped($item[batblade])) add_melee=add_melee+15 ;
if(have_equipped($item[mohawk wig])) add_melee=add_melee+10 ;
if(have_equipped($item[pitchfork])) add_melee=add_melee+9 ;
if(have_equipped($item[ridiculously huge sword])) add_melee=add_melee+10 ;
if(have_equipped($item[denim axe])) add_melee=add_melee+5 ;
if(have_equipped($item[hamethyst necklace])) add_melee=add_melee+5 ;
if(have_equipped($item[ring of increase damage])) add_melee=add_melee+5 ;
if(have_equipped($item[9-ball])) add_melee=add_melee+5 ;
if(have_equipped($item[ninja mop])) add_melee=add_melee+4 ;
if(have_equipped($item[sabre teeth])) add_melee=add_melee+4 ;
if(have_equipped($item[knob goblin elite polearm])) add_melee=add_melee+3 ;
return add_melee;
}
int get_elemental(string what_type)
{
int hot_damage=0;
int cold_damage=0;
int spooky_damage=0;
int sleaze_damage=0;
int stench_damage=0;
int elemental=0;
//This will try to get all elemental effects. Since some apply to both, and some only Melee/Spells, it's all one function
//hot first
if(have_effect($effect[heart of orange])>0) hot_damage=hot_damage+5;
if(have_equipped($item[5-ball])) hot_damage=hot_damage+5;
if(have_effect($effect[hot blooded])>0) hot_damage=hot_damage+10;
if(what_type="melee")
{
if(have_effect($effect[flaming weapon])>0) hot_damage=hot_damage+3;
if(have_equipped($item[flaming talons])) hot_damage=hot_damage+5;
if(have_equipped($item[demon whip])) hot_damage=hot_damage+3;
if(have_equipped($item[icy-hot katana])) hot_damage=hot_damage+3;
}
if(what_type="sauce")
{
if(have_equipped($item[demonskin jacket])) hot_damage=hot_damage+5;
if(have_equipped($item[foon of fulmination])) hot_damage=hot_damage+8;
if(have_equipped($item[ram-battering staff])) hot_damage=hot_damage+10;
if(have_equipped($item[Codex of Capsaicin Conjuration])) hot_damage=hot_damage+10 ;
if(have_equipped($item[enchantlers])) hot_damage=hot_damage+10 ;
}
//cold
if(have_effect($effect[cold blooded])>0) cold_damage=cold_damage+10;
if(have_equipped($item[2-ball])) cold_damage=cold_damage+5;
if(what_type="melee")
{
if(have_equipped($item[icy-hot katana])) cold_damage=cold_damage+3;
if(have_equipped($item[projectile icemaker])) cold_damage=cold_damage+5;
if(have_effect($effect[frigid weapon])>0) cold_damage=cold_damage+3;
}
if(what_type="sauce")
{
if(have_equipped($item[foon of frigidity])) cold_damage=cold_damage+8;
if(have_equipped($item[Gazpacho's Glacial Grimoire])) cold_damage=cold_damage+10 ;
}
//spooky
if(have_effect($effect[spooky demeanor])>0) spooky_damage=spooky_damage+10;
if(have_equipped($item[3-ball])) spooky_damage=spooky_damage+5;
if(have_effect($effect[dirge of dreadfulness])>0) spooky_damage=spooky_damage+12;
if(what_type="melee")
{
if(have_effect($effect[spooky weapon])>0) spooky_damage=spooky_damage+3;
if(have_effect($effect[snarl of the timberwolf])>0) spooky_damage=spooky_damage+10;
if(have_equipped($item[clownskin harness])) spooky_damage=spooky_damage+2;
if(have_equipped($item[lupine sword])) spooky_damage=spooky_damage+9;
if(have_equipped($item[spiked femur])) spooky_damage=spooky_damage+6;
if(have_equipped($item[shuddersword])) spooky_damage=spooky_damage+5;
}
if(what_type="sauce")
{
if(have_equipped($item[foon of fearfulness])) spooky_damage=spooky_damage+8;
}
//sleaze
if(have_equipped($item[4-ball])) sleaze_damage=sleaze_damage+5;
if(have_effect($effect[supafly])>0) sleaze_damage=sleaze_damage+10;
if(what_type="melee")
{
if(have_equipped($item[clownskin harness])) sleaze_damage=sleaze_damage+2;
if(have_equipped($item[demon whip])) sleaze_damage=sleaze_damage+3;
if(have_effect($effect[sleazy weapon])>0) sleaze_damage=sleaze_damage+3;
if(have_effect($effect[amorous])>0) sleaze_damage=sleaze_damage+5;
}
if(what_type="sauce")
{
if(have_equipped($item[foon of fleshiness])) sleaze_damage=sleaze_damage+8;
if(have_equipped($item[giant cheesestick])) sleaze_damage=sleaze_damage+11;
}
//stench
if(have_effect($effect[stenchtastic])>0) stench_damage=stench_damage+10;
if(have_equipped($item[6-ball])) stench_damage=stench_damage+5;
if(what_type="melee")
{
if(have_effect($effect[stinky weapon])>0) stench_damage=stench_damage+3;
if(have_equipped($item[demon whip])) stench_damage=stench_damage+3;
}
if(what_type="sauce")
{
if(have_equipped($item[foon of foulness])) stench_damage=stench_damage+8;
if(have_equipped($item[smoldering staff])) stench_damage=stench_damage+11;
}
// calculating elemental bonus for sauce before weaknesses, since that will be dealt with for the whole spell.
if(what_type="sauce" && have_skill($skill[immaculate seasoning])) elemental=max(hot_damage, cold_damage);
if(what_type="sauce" && !have_skill($skill[immaculate seasoning])) elemental=min(hot_damage, cold_damage); //best to assume the worst
//now to consider the weaknesses
if(bVerbose) print("Monster defence is "+element_to_string(monster_defense_element()));
if(monster_defense_element()= $element[hot])
{
stench_damage=stench_damage*2;
sleaze_damage=sleaze_damage*2;
hot_damage=1;
}
if(monster_defense_element()= $element[cold])
{
hot_damage=hot_damage*2;
spooky_damage=spooky_damage*2;
cold_damage=1;
}
if(monster_defense_element()= $element[spooky])
{
stench_damage=stench_damage*2;
hot_damage=hot_damage*2;
spooky_damage=1;
}
if(monster_defense_element()= $element[sleaze])
{
cold_damage=cold_damage*2;
spooky_damage=spooky_damage*2;
sleaze_damage=1;
}
if(monster_defense_element()= $element[stench])
{
cold_damage=cold_damage*2;
sleaze_damage=sleaze_damage*2;
stench_damage=1;
}
if(what_type="melee") elemental = hot_damage+cold_damage+spooky_damage+sleaze_damage +stench_damage;
if(dbug) print("You have "+int_to_string(elemental)+" elemental damage to add for "+what_type );
return elemental;
}
int get_offhand()
{
int offhand_weapon_damage=0;
if(get_power(current_equipment($slot[off-hand]))>0 && !ShieldEquipped())
{
if(bVerbose) print("Weapon in offhand");
offhand_weapon_damage=get_power(current_equipment( $slot[off-hand]))/10;
}
return offhand_weapon_damage;
}
skill Choose_skill(int hp_target, boolean hitting_allowed)
{
// LTS/TS formulae are from HCO forums, courtesy of Strangerer
// Spell bonuses - sauce only, no check for cookbook or flavour of magic as these are unlikely!
int spicebonus=get_spell_bonus("sauce");
int pastabonus=get_spell_bonus("pasta");
int spell_element = get_elemental("sauce");
int expectdam=0;
int my_myst=my_buffedstat($stat[mysticality]);
skill To_use=$skill[none];
boolean sauce_resistant=false;
if(monster_defense_element()==$element[cold])
{
if(!have_skill($skill[immaculate seasoning]) && !have_equipped($item[Gazpacho's Glacial Grimoire]))
{
sauce_resistant=true; //risk of doing minimal damage with sauce
if(dbug) print("Risk of doing minimal damage, ignoring sauce spells");
}
}
if(monster_defense_element()==$element[hot])
{
if(!have_skill($skill[immaculate seasoning]) && !have_equipped($item[Codex of Capsaicin Conjuration]))
{
sauce_resistant=true; //risk of doing minimal damage with sauce
if(dbug) print("Risk of doing minimal damage, ignoring sauce spells");
}
}
boolean sauce_weak=false; //check if the spell you're casting hits a weakness - only if guaranteed
if(monster_defense_element()==$element[sleaze] ||monster_defense_element()==$element[stench])
{
if(have_skill($skill[immaculate seasoning]) || have_equipped($item[Gazpacho's Glacial Grimoire]))
{
sauce_weak=true;
if(dbug) print("Monster is weak to your spells!");
}
}
if(monster_defense_element()==$element[cold] ||monster_defense_element()==$element[spooky])
{
if(have_skill($skill[immaculate seasoning]) || have_equipped($item[Codex of Capsaicin Conjuration]))
{
sauce_weak=true;
if(dbug) print("Monster is weak to your spells!");
}
}
int melee_bonus=get_melee_bonus(); //only for physical attacks
int melee_elemental=get_elemental("melee");
int offhand_damage=get_offhand();
record kill_it { skill name; int spelldam; } ;
kill_it [int] damage_dealers;
damage_dealers [1].name = $skill[saucegeyser]; // 40 mp
damage_dealers [1].spelldam = (my_myst*0.35)+35 + spicebonus + spell_element;
damage_dealers [2].name = $skill[weapon of the pastalord]; // 35 mp
damage_dealers [2].spelldam = (my_myst*.35)+32 + pastabonus;
damage_dealers [3].name = $skill[wave of sauce]; // 23 mp
damage_dealers [3].spelldam = min((my_myst*0.3)+20,50) + spicebonus + spell_element;
damage_dealers [4].name = $skill[cone of whatever]; // 19 mp
damage_dealers [4].spelldam = min((my_myst*.25)+16,46) + pastabonus;
damage_dealers [5].name = $skill[saucestorm]; // 12 mp
damage_dealers [5].spelldam = min((my_myst/5)+14,39) + spicebonus + spell_element;
if(hitting_allowed && current_hit_stat() == $stat[muscle])
{
damage_dealers [6].name = $skill[lunging thrust-smack]; // 8 mp
damage_dealers [6].spelldam = (((get_power(current_equipment($slot[weapon]))/10)+melee_bonus)*3) + melee_elemental + min(0,(my_buffedstat($stat[muscle])-monster_defense()))+offhand_damage;
}
damage_dealers [7].name = $skill[extreme ray of something]; // 7 mp
damage_dealers [7].spelldam = min((my_myst*.15)+8,28) + pastabonus;
damage_dealers [8].name = $skill[minor ray of something]; // 4 mp
damage_dealers [8].spelldam = min((my_myst*.07)+3,18) + pastabonus;
damage_dealers [9].name = $skill[stream of sauce]; // 3 mp
damage_dealers [9].spelldam = min((my_myst/10)+3,18) + spicebonus + spell_element;
if(hitting_allowed && current_hit_stat() == $stat[muscle])
{
damage_dealers [10].name = $skill[thrust-smack]; // 3 mp
damage_dealers [10].spelldam = (((get_power(current_equipment($slot[weapon]))/10)+melee_bonus)*2)+ melee_elemental + min(0,(my_buffedstat($stat[muscle])-monster_defense()))+offhand_damage;
}
//allowance for elemental spells here - Sauce only
if(sauce_resistant)
{
damage_dealers [1].spelldam = 1; //Saucegeyser
damage_dealers [3].spelldam = 1; //Wave
damage_dealers [5].spelldam = 1; //Storm
damage_dealers [9].spelldam = 1; //Stream
}
if(sauce_weak)
{
damage_dealers [1].spelldam = 2*((my_myst*0.35)+35 + spicebonus + spell_element); //Saucegeyser
damage_dealers [3].spelldam = 2*(min((my_myst*0.3)+20,50) + spicebonus + spell_element); //Wave
damage_dealers [5].spelldam = 2*(min((my_myst/5)+14,39) + spicebonus + spell_element); //Storm
damage_dealers [9].spelldam = 2*(min((my_myst/10)+3,18) + spicebonus + spell_element); //Stream
}
skill possible;
int possible_dam;
foreach key in damage_dealers
{
possible = damage_dealers[key].name;
possible_dam = damage_dealers[key].spelldam;
// Print("Casting "+skill_to_string(damage_dealers[key].name)+" should do "+ int_to_string(damage_dealers[key].spelldam));
if(checkSkill(possible)==0)
{
if(possible_dam >= hp_target)
{
To_use=possible;
expectdam = possible_dam;
if(bVerbose) print(skill_to_string(To_use)+" should do "+int_to_string(expectdam)+" damage.");
}
}
}
return To_use;
}
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;
}
void Kill(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 hp_target=monster_hp();
if(dbug) print("Kill: need to do " + hp_target + " points of damage in one turn.");
boolean LTS_safe = true;
// eek==$monster[Apathetic Lizardman] || to be added back
if(eek==$monster[Procrastination Giant] || eek==$monster[acid blob] || CheckPhysicalResistance(eek) || gbEnemyNoodled)
{
LTS_safe = false;
if(dbug) Print("Don't hit it!");
}
skill whichSkill=$skill[none];
Whichskill = Choose_skill(hp_target, LTS_safe);
//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(WhichSkill==$skill[none] && CheckSkill($skill[entangling noodles])==0 && bUsedNoodles==false && giRound<27)
{
if(dbug) Print("No 1-Hit Kill. Noodling");
use_skill($skill[entangling noodles]);
set_property("IllNeoStasis_UsedNoodles", "true");
set_property("IllNeoStasis_EnemyNoodled", "true");
return;
}
if(WhichSkill=$skill[none]) //no 1-hit kill, check for 2-hit
{
hp_target=hp_target/2;
Whichskill = Choose_skill(hp_target, LTS_safe);
}
//We should have a skill now, for a 1 or 2-hit kill
if(WhichSkill !=$skill[none])
{
if(dbug) print("Casting "+skill_to_string(WhichSkill));
use_skill(WhichSkill);
}
else
{
if(CheckSkill($skill[stream of sauce])==0 && giRound<=27) //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)
{
whichSkill=$skill[cleesh];
}
else
{
cli_execute("Abort Kill it now!");
}
}
}
}
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_attack();
iMoxPenalty = max(iAtk - my_buffedstat(gDefenceStat),0);
iDam = 0.25*iAtk + iMoxPenalty;
iDam = iDam - damage_reduction()-LeatherbackDR();
iDam = iDam - (iDam * damage_absorption_percent()/100);
iDam = iDam- (iDam * elemental_resistance(monster_attack_element())/100) ;
if(bVerbose) print("You have "+int_to_string(elemental_resistance(monster_attack _element()))+"% 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
{
return ((monster_attack() + OVERMOXIE_LEVEL) <= my_buffedstat($stat[moxie]) );
}
boolean PlayerCannotHit(monster eek)
//Test if we can hit the mob
{
return (my_buffedstat(current_hit_stat()) < (monster_defense()+ 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
{
Kill(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[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_defense() < -5)
{
#attack and miss
attack();
}
else
//nothing we can do except throw junk, or attack and hit
{
if( eek =$monster[procrastination giant] )
{ kill(eek); }
else {
if (!ThrowJunkItem())
{ attack(); }
}
}
}
}
}
}
}
}
void main(int iRound, monster eek, string sText)
{
// Check this script is worth running!
if(have_effect($effect[temporary amnesia])>0)
{ cli_execute("abort You've forgotten how to do this!"); }
if(have_effect($effect[Jabaņero Saucesphere])==0)
{ cli_execute("abort Oops! You forgot the sauce."); }
//Check if we're poisoned, and don't use our last anti-anti-antidote if we are
if(have_effect($effect[hardly poisoned at all])> 0 || have_effect($effect[somewhat poisoned])> 0 || have_effect($effect[a little bit poisoned])> 0)
{
if(item_amount($item[anti-anti-antidote])<=1)
{
USE_ANTIDOTE=FALSE;
}
}
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
// removed percentages because I prefer absolute numbers - BD
//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() + ")"); }
if (dbug) { print("HP=" + my_hp() + "/" + my_maxhp()); }
if (dbug) { print("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 (my_MP() >= MANA_MAX)
{
//
if (dbug) { print("We're full on MP!"); }
if (my_hp()< my_maxhp()-15 && CheckSkill($skill[saucy salve]) == 0)
{
// If we are below max 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 ||string_to_int(get_property("IllNeoStasis_MaxDamTaken"))>= DAM_THRESHOLD || ExpectedMonsterDamage(eek)>=DAM_THRESHOLD)
//Too painful! End fight quickly
{
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=="finish") {
sAction="Finish";
} else {
if(safetest=="finishNOW")
{
if(gbEnemyNoodled)
{
sAction="Finish";
}
else
{
if(checkSkill($skill[entangling noodles])==0 && !bUsedNoodles)
{
sAction="Noodle";
}
else
{
sAction="Finish";
}
}
}
}
}
if (ExpectedDamage > (my_hp() + SAFETY_NET))
//danger of death - panic
{
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 = "Finish";
}
}
else
{
if (CheckSkill($skill[entangling noodles])==0 && !bUsedNoodles)
{
sAction = "Noodle";
}
else
{
sAction = "Finish";
}
}
}
}
if (iRound >= 29)
{
#long enough!
sAction = "Finish";
}
ShowStatusMsg("Action = " + sAction);
//And finally, perform our action
if (sAction == "Attack") attack();
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="Finish";
}
if (sAction == "Miss") DontHurtHim(eek);
if (sAction == "Pickpocket") Pickpocket();
if (sAction == "Noodle")
{
use_skill($skill[entangling noodles]);
set_property("IllNeoStasis_UsedNoodles", "true");
set_property("IllNeoStasis_EnemyNoodled", "true");
}
}
gemelli
04-29-2007, 04:32 AM
So far, this is looking great! I'll try to find time to play with it over the next few days; my initial reactions are all based on reading, not running. That being said, here are a few things that jumped out at me from an initial review:
(1) get_spell_bonus(): Intrinsic spiciness and Immaculate seasoning only give bonuses to Sauceror spells. You might want to consider adding a what_type parameter to the function call (as you've done below in get_elemental) to handle this.
(2) Choose_skill(): Do you really need to pass WhichSkill and my_myst as parameters to this function? You start the function by setting WhichSkill to $skill[none], which I think eliminates any benefit to having it as a passed variable :) And of course, my_myst can be deduced by a native ASH function call from within this function. But overall, I really, really like this function. (Have you considered moving the CLEESH/stream fallback option selection from the end of the Kill() function to this one?)
(3) main(): This is partly my fault, but please remove the colon after the two instances of "abort:" at the start of the main function.
(4) Kill(): I'd add the Apathetic Lizardman and the Acid Blob as two more monsters you don't want to hit with physical attacks.
(5) StasisSafe(): I think this function can now be greatly simplified, since the only interesting results coming out of it should be "finish" or "safe." This applies to the handling of StasisSafe's results in main() as well.
BDrag0n
04-29-2007, 12:25 PM
(1) get_spell_bonus(): Intrinsic spiciness and Immaculate seasoning only give bonuses to Sauceror spells. *You might want to consider adding a what_type parameter to the function call (as you've done below in get_elemental) to handle this. Oops, I wasn't adding *any* spell bonus to pasta spells. Fixed.
(2) Choose_skill(): Do you really need to pass WhichSkill and my_myst as parameters to this function? *You start the function by setting WhichSkill to $skill[none], which I think eliminates any benefit to having it as a passed variable :) *And of course, my_myst can be deduced by a native ASH function call from within this function. *But overall, I really, really like this function. *(Have you considered moving the CLEESH/stream fallback option selection from the end of the Kill() function to this one?)
I guess not. WhichSkill being set to none at the start of the function was an attempt to prevent it looping if you've not enough mana to use any of the selected skills - haven't had a chance to test if that works yet!
I'm not moving the fallback options in here because the function is called twice, the second time is to look at a 2-hit kill if a 1-hit isn't possible. Moving the fallback options in here would mean it *always* returns something, so would never look at a 2-hit kill.
(3) main(): This is partly my fault, but please remove the colon after the two instances of "abort:" at the start of the main function. Done. I guess it's aesthetics - it looks better in the script with the colon, but better in the gCLI without ::)
(4) Kill(): I'd add the Apathetic Lizardman and the Acid Blob as two more monsters you don't want to hit with physical attacks. Acid blod added, but Mafia failed to recognise the Lizardman - I'll report that and add it back ASAP.
(5) StasisSafe(): I think this function can now be greatly simplified, since the only interesting results coming out of it should be "finish" or "safe." *This applies to the handling of StasisSafe's results in main() as well.
I've left "finishNOW" as well, since that noodles before trying to kill.
Changes have been edited into the post above.
BDrag0n
04-29-2007, 01:17 PM
int monster_attack(): Added
int monster_defense(): Added
int monster_hp(): Added
I assume the above are meant to be "current" stats, bearing in mind current +ML? Quick testing shows that the monster_attack() at least is giving the same as monster_base_attack(). Monster_hp() does give the current value, I believe.
holatuwol
04-29-2007, 04:02 PM
I assume the above are meant to be "current" stats, bearing in mind current +ML?
I forgot that ML adjusted those values. Fixed.
gemelli
05-03-2007, 05:35 PM
A few minor changes in this version:
# v2.1- Added tracker for damage dealt via spheres, reducing MP requirements for Kill(). Added
# "poke" action to address cases where we have full MP, full HP, and want to do something --
# previously, Kill() would be called at a potentially high MP cost. Added debug() function
# to simplify debugging calls.
I noticed that Kill() was defaulting to overkill spells a little too often for my tastes, so this was my attempt to rein that function in a bit :)
Let me know if you have any questions/comments.
macman104
05-03-2007, 06:31 PM
I just wanted to say that while I have not used, nor plan to use this script right now, it is by far one of the more impressive scripts, and I've really liked watching it develop.
BDrag0n
05-03-2007, 08:35 PM
A few minor changes in this version:
Yeah, the "poke" is a good call - I hadn't noticed because I've only *got* stream and LTS <g>.
Re: tracker for sphere damage - my understanding is that monster_hp() returns the monster's current HP, including damage done - so I'm not sure about this - surely it would result in double-counting damage from spheres?
That being said I've failed to track down a bug where Kill() drastically under-counted the damage to be done, which I had thought to be +ML related but now can't duplicate :-(
gemelli
05-03-2007, 09:27 PM
Yeah, the "poke" is a good call - I hadn't noticed because I've only *got* stream and LTS .
Trust me, the first time you cast Wave of Sauce on round 29 vs. a drunk goat, you will sit up and notice :D
Re: tracker for sphere damage - my understanding is that monster_hp() returns the monster's current HP, including damage done - so I'm not sure about this - surely it would result in double-counting damage from spheres?
Nope ... I just added a test line to the round info area -- print("Monster HP: "+monster_hp()); -- and ran the script in the Friars'. *Although I was dealing damage every round, monster_hp() did not change. *So that function seems to return the base HP for the monster, not current.
That being said I've failed to track down a bug where Kill() drastically under-counted the damage to be done, which I had thought to be +ML related but now can't duplicate :-(
Hm, I'll keep an eye out for that one. *Hopefully, the debug line I added near the front of Kill() should help a little.
illarion
05-04-2007, 12:38 AM
Had a little time to do some testing. This is for v2, since I hadn't seen 2.1 yet :)
v2 got stuck looping round and round. gCLI output was:
++++++++++++++++++++++++++++++++
Round 29 vs irritating series of random encounters
++++++++++++++++++++++++++++++++
Valley=false
HP=21/247 MP=71/112
You have 0% elemental resistance
Round: 29 (MP Change=48, HP Change=-122) --> Action = Finish
Finish Him!
Kill: need to do 140 points of damage in one turn.
Don't hit it!
Monster defence is none
You have 0 elemental damage to add for sauce
Monster defence is none
You have 5 elemental damage to add for melee
Monster defence is none
You have 0 elemental damage to add for sauce
Monster defence is none
You have 5 elemental damage to add for melee
Offhand=box turtle-> Shield=true IllNeoStasis_Hero => true
You have 0% elemental resistance
Expected damage based on this fight is: 12
First, 140 points to kill it is wrong - way wrong. (28 rounds of jala + jaba had gone off).
This implies that monster_hp() is not tracking this damage.
Second, the kill logic is obviously getting confused. I have LTS and TS, but no sauce spells - the correct behaviour would be to LTS if it will kill, or TS if not. (Given round 29, I've been stasising, and TS should be fine)
The only program flow that I can see leading to the "Don't hit it!" message is triggered either by specific monsters (not this one though), or the enemy being noodled. I can't tell if it is from the gCLI output (it overflowed), but the settings file suggests it.
Even if this is just bad luck (happening to noodle due to damage right when we ran out of rounds), LTS is still the correct course of action. I'd suggest removing the enemynoodled -> LTS_safe = false logic, except perhaps in the first 5 rounds or something?
----
Started looping again, against a protagonist this time. Definitely noodled - basically, the script doesn't know how to deal with a noodled opponent if you don't have sauce spells.
holatuwol
05-04-2007, 12:50 AM
What does sphere damage look like?
gemelli
05-04-2007, 05:39 PM
What does sphere damage look like?
Your Jabaņero Saucesphere does 5 damage to your opponent, and infuses you with renewed energy.<center><Table><tr><td>http://images.kingdomofloathing.com/itemimages/mp.gif</td><td valign=center class=effect>You gain 5 Muscularity Points.</td>
That's the code I'm parsing in v2.1, which detects Jabanero but not Jalapeno damage (I rarely use Jalapeno, so it isn't as important to me).
holatuwol
05-04-2007, 06:32 PM
Odd, it should be counting that. o_0 When I perm Jabaņero this coming run, I'll look into it.
Veracity
05-04-2007, 07:04 PM
When I perm Jabaņero this coming run, I'll look into it.
Great minds think alike; I bought Jabaņero this run and will perm it when I ascend - in about a month, since I am adventuring my way to L30 and will be gone for a week in the middle. And then on to get other basic stasis skills... :)
BDrag0n
05-04-2007, 08:52 PM
Trust me, the first time you cast Wave of Sauce on round 29 vs. a drunk goat, you will sit up and notice :D I'll believe it! <g>
Nope ... I just added a test line to the round info area -- print("Monster HP: "+monster_hp()); -- and ran the script in the Friars'. *Although I was dealing damage every round, monster_hp() did not change. *So that function seems to return the base HP for the monster, not current.
Odd - the line print("Monster has "+monster_hp()+" HP left"); at the start of the round which I was using shows the monster HP decreasing every turn. Are you using the current SVN or the public release?
Hm, I'll keep an eye out for that one. *Hopefully, the debug line I added near the front of Kill() should help a little.
Ta. I need to spend more time on finding it, but RL is short of that right now.
gemelli
05-04-2007, 09:38 PM
Odd - the line print("Monster has "+monster_hp()+" HP left"); at the start of the round which I was using shows the monster HP decreasing every turn. Are you using the current SVN or the public release?
The public release, 11.0. Perhaps something was changed in a more recent build? In any case, hmm.
BDrag0n
05-04-2007, 10:53 PM
Definitely noodled - basically, the script doesn't know how to deal with a noodled opponent if you don't have sauce spells.
Strange, I can't find any reason for the loop - if you've no sauce spells, it should return $skill[none] and either cleesh it or abort. Cleesh might not be the best choice, but it should still be doing *something* - even if it's just aborting. However, removing the noodle check from LTS safe is probably the quickest and easiest option - if you can 1-hit an LTS is fine. The risk then is that TS/LTS might be preferred for a 2-hit kill, when the enemy is noodled - however if I add the noodle check in just as the 2-hit logic starts, that should be OK. The relevant section of code would look like
boolean LTS_safe = true;
if(eek==$monster[Procrastination Giant] ||eek==$monster[Apathetic Lizardman] || eek==$monster[acid blob] || CheckPhysicalResistance(eek))
{
LTS_safe = false;
if(dbug) Print("Don't hit it!");
}
skill whichSkill=$skill[none];
Whichskill = Choose_skill(hp_target, LTS_safe);
//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(WhichSkill==$skill[none] && CheckSkill($skill[entangling noodles])==0 && bUsedNoodles==false && giRound<27)
{
if(dbug) Print("No 1-Hit Kill. Noodling");
use_skill($skill[entangling noodles]);
set_property("IllNeoStasis_UsedNoodles", "true");
set_property("IllNeoStasis_EnemyNoodled", "true");
return;
}
if(WhichSkill=$skill[none]) //no 1-hit kill, check for 2-hit
{
if(gbEnemyNoodled) LTS_safe=false;)
hp_target=hp_target/2;
Whichskill = Choose_skill(hp_target, LTS_safe);
}
What do you think?
illarion
05-05-2007, 04:13 AM
Looks good, from a quick skim. Will give it a try next run :)
BDrag0n
05-06-2007, 03:56 PM
The public release, 11.0. *Perhaps something was changed in a more recent build? *In any case, hmm.
Yeah, Hmmm - I'm now seeing the same behaviour, which is very strange to say the least.
Also boo! Anti-dotes now no longer w*rk for this script.
DarthDud
05-06-2007, 10:27 PM
Also boo! Anti-dotes now no longer w*rk for this script.
Apparently if you use a penguin contract without a penguin equiped, it does the same thing as antidotes used to do.
BDrag0n
05-07-2007, 12:27 AM
Apparently if you use a penguin contract without a penguin equiped, it does the same thing as antidotes used to do.
Yep, Gemelli added that in 1.7, I think - but a) I'm not sure it works anymore following the changes and b) I don't have a penguin to test a).
DarthDud
05-07-2007, 01:25 AM
It does work... for now...
illarion
05-07-2007, 01:53 AM
So what, have antidotes been fixed in-game? What happens now if you try to use one? Just a thumb twiddle?
(Spot the guy with no time to play :( ).
gemelli
05-07-2007, 02:10 PM
Well, good news and bad news. The good news: using the contracts does indeed work for now. The bad news: Mafia assumes that the contract has been used up after the first time you try to throw it. So you get exactly one combat round of doing nothing in this script, and then you're back to throwing junk. And you have to refresh your inventory between battles to get even that one round of use.
Any ideas for working around this?
efilnikufecin
05-07-2007, 02:13 PM
Any ideas for working around this?
directly invoke the url via visit_url?
gemelli
05-07-2007, 02:17 PM
My temporary workaround, which assumes that you've put a contract in your inventory already:
Changes to config section:
//Change to false if goodfella contract is changed!
boolean USE_GOODFELLA = true;
boolean gf_override=true;
Changes to DontHurtHim():
if ((item_amount($item[goodfella contract])>0 || gf_override) && USE_GOODFELLA && my_familiar()!=$familiar[Penguin Goodfella])
In other words, as long as I have gf_override set to true, it'll try to use the contract even if Mafia doesn't think I have one. *It's a hack, but it works.
EDIT: Well, they just fixed the Goodfella loophole, too. It was nice while it lasted, anyway :)
illarion
05-08-2007, 04:40 PM
Crap, so, the antidote, joybuzzer and contract stuff needs to go... does the dictionary still work?
/em grumbles about bugfixes causing rewrites
Are either of my fellow scripters eager to do these changes? I might get a chance to do a little on it over the next day or two, with luck, otherwise :)
gemelli
05-08-2007, 08:54 PM
The dictionary still works, yup. It's the only thing I know of that does.
I'm not likely to find time to work on it myself until Thursday at the earliest. If either of you are so inclined, please feel free :)
BDrag0n
05-08-2007, 09:16 PM
As requested - this includes some not-tested work on familiar stuff. Basically, I'm adding the potential familiar MP restores from Lion & Hobo in the MANA_MAX check, and a nice red-text warning that the hobo is out of booze.
I've added an option to turn those checks off, though.
BD.
Edit: I also set the gf_override to false!
illarion
05-08-2007, 11:01 PM
Thanks BDragon, I'll give it a try tomorrow.
I've been meaning to have a poke, but my version is all out of sync, so will be good to work from a common base.
gemelli
05-08-2007, 11:19 PM
I like the new version from a read-only perspective (haven't tested yet). A few nits:
(1) We can take out all the Goodfella stuff, too :(
(2) I really like the MANA_MAX changes you added. Can I suggest two more:
if(have_effect($effect[purple tongue]) > 0) MANA_MAX=MANA_MAX-20;
if(have_effect($effect[heart of orange]) > 0) MANA_MAX=MANA_MAX-3;
(3) Change "scroll of turtle summoning" to "turtle totem" throughout.
Danke!
BDrag0n
05-09-2007, 02:28 PM
OK, this includes the changes suggested by Gemelli - but is untested since I can't get access to KoL at w*rk. It will likely not be functional in any except the latest SVN build, as mafia will not recogise "turtle totem" as a combat-usable item.
there again, it might!
Edit: As I said, untested - replaced with a version that at least verifies correctly!
illarion
05-11-2007, 04:46 PM
It won't download for me - though previous attachments do. Could you repost please?
BDrag0n
05-11-2007, 05:49 PM
Of course!
Mighty Xerxes has raised questions about adding delevelling options - but it'll be a while before I have any time to devote to that, so this is just as previous:
illarion
05-28-2007, 04:31 PM
Well, guess there's no point doing any further work on this now. It was fun while it lasted though!
R.I.P. Stasis.
gemelli
05-29-2007, 10:15 PM
Indeed it was :) Thanks to you and BDrag0n for your continued support of this script; it was a ton of fun to work on.
BDrag0n
05-29-2007, 10:38 PM
I have to agree, Gemelli - this has been a fun and interesting project, and I think I've just about made up the time spent working on the script by the time saved by the script :P - and had a lot of fun, trying to teach a computer to stasis properly (and learning a lot about both ASH and stasis in the process!)
illarion
05-29-2007, 11:27 PM
Well, thanks to both of you :) Not only did your input and constructive criticism make it (much) more interesting developing the script, but you both made seriously good additions to the functionality.
This being my first experience of (sort of) open-sourcing something I've written - even if it was just a script - was really positive.
Let's hope there's something worth automating with NS13, and we can all put our heads together and teach Mafia to do it ;)
macman104
05-30-2007, 05:27 AM
Just wanted to say I liked watching this script being made. Hopefully you'll be able to pull out something equally cool for NS-13 :)
illarion
07-02-2007, 10:51 AM
Thanks :) Would have to be pretty cool indeed to get the same kind of results these days... ::) ;)
DarthDud
07-18-2007, 03:01 PM
Hmmmm...
So, how hard would it be to adapt this script for bakula-stasis?
illarion
07-18-2007, 03:21 PM
Well, loan me a bakula, and I could give it a go :)
Seriously, probably not too hard - though of course Mafia will be lacking info on the new enemies and so on for some time yet.
DarthDud
07-19-2007, 07:05 AM
Yeah, we could probably sent up a bakula loaner, if you need it.
Here's how the bakula works, exactly, to the best of my knowledge (and personal experience). All possible results uniformly distributed.
Damage HP Restore MP Restore
11 5 6
12 6 6
13 6 7
14 7 7
15 7 8
16 8 8
17 8 9
18 9 9
19 9 10
20 10 10
For 15.5 average damage, 7.5 average HP restore, and 8 average MP restore, per turn.
Another thing to note is that against physical resistant mosnters, the bakula only does 1 damage, but still restores fully. Also, bakula does not break noodles.
[/begzor] ;)
illarion
07-19-2007, 12:22 PM
So what combat are you looking for here? Bear in mind I've literally never done a softcore run - I have no idea what it's like with the best equipment and extreme ML.
Is it as simple as using vampiric urges over and over, with noodles and/or finisher when needed to avoid death?
DarthDud
07-20-2007, 06:50 AM
So what combat are you looking for here?
Is it as simple as using vampiric urges over and over, with noodles and/or finisher when needed to avoid death?
Pretty much, yes. All the logic should pretty much be in place from the stasis script, like the damage prediction mechanism, and panic/kill-monster mechanisms. The only real differences I can think of is that it would need to account for a minimum (to be safe) bakula restore each round, so that it didn't decide to kill the monster a round sooner than necessary; that the bakula damage is different from sphere damage; and that you wouldn't want to ever cast healing skills in battle. And I suppose the panic/kill mechanism altered a little to account for the combat skill changes.
But yes, it would basically be as simple as noodles, then urges over and over, until HP is going low and it needs to kill the monster.
illarion
07-25-2007, 03:55 PM
OK, well, I'm happy to have a go at it, but it won't be too soon I'm afraid. Lots of RL stuff on at present - don't even have time to play KoL :(
Powered by vBulletin® Version 4.2.0 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.