/* * mathscript.cpp by Keith Fulton * * Copyright (C) 2004 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER) # undef new # if defined(CS_EXTENSIVE_MEMDEBUG) # undef CS_EXTENSIVE_MEMDEBUG # endif # if defined(CS_MEMORY_TRACKER) # undef CS_MEMORY_TRACKER # endif #endif #include #include #include "util/psstring.h" #include "util/log.h" #include #include #include #include #include "util/fparser.h" #include "util/mathscript.h" #include "util/strutil.h" #include "util/consoleout.h" #include "../../server/bulkobjects/pscharacter.h" #include "../../server/bulkobjects/psitem.h" MathScriptLine::MathScriptLine(const char *line,MathScript *myScript) { scriptLine = line; csString var = GetWordNumber(scriptLine,1); assignee = myScript->GetOrCreateVar(var); valid = true; var_array = NULL; size_t start = scriptLine.FindFirst('='); scriptLine.DeleteAt(0,start+1); fp.AddFunction("rnd", MathScriptEngine::RandomGen, 1); fp.AddFunction("pow", MathScriptEngine::Power, 2); fp.AddFunction("getSkillValue", MathScriptEngine::GetSkillValue, 2); fp.AddFunction("getAverageSkillValue", MathScriptEngine::GetAverageSkillValue, 4); fp.AddFunction("getStatValue", MathScriptEngine::GetStatValue, 2); fp.AddFunction("getArmorVSWeaponResistance", MathScriptEngine::GetArmorVSWeaponResistance, 2); ParseVariables(myScript); ParseFormula(myScript); } MathScriptLine::~MathScriptLine() { if (var_array) delete[] var_array; } MathScriptVar *MathScriptLine::FindVariable(const char *name) { size_t i; for (i=0; iname == name) return variables[i]; } return NULL; } void MathScriptLine::ParseVariables(MathScript *myScript) { size_t start=0; psString word; while ( start < scriptLine.Length() ) { scriptLine.GetWord(start,word,false); if (!word.Length()) { start++; continue; } if (isupper(word.GetAt(0))) // capital first letter means var name { // A compound var function starts as Target:WeaponAt(Slot) // and turns into WeaponAt(Target,Slot) // A compound var turns Target::HP into Target_HP if (scriptLine.Length() > start+word.Length() && scriptLine[start+word.Length()] == ':') // compound var name { MathScriptVar *base = FindVariable(word); // get core variable onto list if (!base) // unique list { base = myScript->GetOrCreateVar(word); variables.Push( base ); } base->type = MathScriptVar::VARTYPE_PTR; psString extend; scriptLine.GetWord(start+word.Length()+2,extend,false); if (scriptLine.Length() > start+word.Length()+extend.Length()+1 && scriptLine[start+word.Length()+1+extend.Length()] == '(') { scriptLine.DeleteAt(start,word.Length()+1); word.Append(','); scriptLine.Insert(start+extend.Length()+1,word); word = ""; } else { MathScriptVar *var = myScript->GetOrCreateVar(word); // get underlying var scriptLine.SetAt(start+word.Length(),'_'); word.Append('_'); word.Append(extend); if (!FindVariable(word)) // unique list { MathScriptVar *ext = myScript->GetOrCreateVar(word); variables.Push( ext ); ext->property = extend; ext->SetVariable(var); } } } else if (!FindVariable(word)) // unique list variables.Push( myScript->GetOrCreateVar(word) ); } start += word.Length(); } if (variables.Length()) var_array = new double[variables.Length()]; } void MathScriptLine::ParseFormula(MathScript *myScript) { csString varlist; varlist = ""; if (variables.Length()) { varlist = variables[0]->name; size_t i; for (i=1; iname); } } size_t ret = fp.Parse( scriptLine.GetData(), varlist.GetData() ); if (ret != (size_t)-1) { valid=false; printf( "Caller: %s", myScript->name.GetData()); Error4("Error in Col %d: %s\n Script: \"%s\" ",ret,fp.ErrorMsg(),scriptLine.GetData() ); } else fp.Optimize(); } void MathScriptLine::Execute() { if (!valid) { Error2("Attempted to execute bad Mathscript line (%s).\n",scriptLine.GetData()); assignee->SetValue(0); return; } size_t i; for (i=0; iGetValue(); assignee->SetValue( fp.Eval(var_array) ); } MathScript::MathScript(iDocumentNode *node) { name = node->GetAttributeValue("name"); ParseScript( node->GetContentsValue() ); } MathScript::MathScript(const char *myname,const char *script) { name = myname; ParseScript(script); } void MathScript::ParseScript(const char *parsescript) { psString script(parsescript); uintptr_t i,line_start = 0; while (line_start < script.Length() ) { i = script.FindFirst(';',line_start); if (i==SIZET_NOT_FOUND) { i = script.Length(); } psString line; if (i-line_start > 0) { script.SubString(line,line_start,i-line_start); // avoids empty lines (spaces, tabs, carriage returns) line.ReplaceAllSubString("\t"," "); line.ReplaceAllSubString("\r"," "); line.ReplaceAllSubString("\n"," "); line = line.Trim(); if (!line.IsEmpty()) { MathScriptLine *newline = new MathScriptLine(line,this); scriptLines.Push(newline); } } line_start = i+1; } } MathScript::~MathScript() { while (scriptLines.Length()) delete scriptLines.Pop(); csHash::GlobalIterator it (variables.GetIterator ()); while (it.HasNext ()) { MathScriptVar* var = it.Next (); delete var; } } MathScriptVar *MathScript::GetVar(const char *name) { unsigned int key = csHashCompute(name); csHash::Iterator iter = variables.GetIterator(key); while (iter.HasNext()) { MathScriptVar *ptr = iter.Next(); if (ptr->name == name) return ptr; } return NULL; } MathScriptVar *MathScript::GetOrCreateVar(const char *name) { MathScriptVar *var = GetVar(name); if (var) return var; // CPrintf(CON_DEBUG, "Creating var <%s> in script <%s>\n",name,this->name.GetData() ); unsigned int key = csHashCompute(name); var = new MathScriptVar; var->name = name; var->SetValue(0); variables.Put(key,var); return var; } void MathScript::Execute() { size_t i; MathScriptVar *exitsignal = GetVar("exit"); if (exitsignal) exitsignal->SetValue(0); // clear exit condition before running for (i=0; iExecute(); if (exitsignal && exitsignal->GetValue()!=0) { // printf("Terminating mathscript at line %d of %d.\n",i, scriptLines.Length()); break; } } } void MathScript::DumpAllVars() { csHash::GlobalIterator iter = variables.GetIterator(); CPrintf(CON_DEBUG, "\nAll vars for '%s'\n-----------------------------------------\n", name.GetData()); while (iter.HasNext()) { MathScriptVar *var = iter.Next(); CPrintf(CON_DEBUG, "%25s=%1.4f\n",var->name.GetData(),var->GetValue() ); } } MathScriptEngine::MathScriptEngine(const char *xmlfile,iVFS *vfs) { csRef xml = csPtr(new csTinyDocumentSystem); csRef buff = vfs->ReadFile( xmlfile ); if ( !buff || !buff->GetSize() ) { return; } csRef doc = xml->CreateDocument(); const char* error = doc->Parse( buff ); if ( error ) { return; } csRef root = doc->GetRoot(); if (!root) return; csRef topNode = root->GetNode("mathscript"); if (!topNode) return; csRef iter = topNode->GetNodes(); while ( iter->HasNext() ) { csRef node = iter->Next(); if (node->GetType() != CS_NODE_ELEMENT ) continue; // This is a widget so read it's factory to create it. if ( strcmp( node->GetValue(), "script" ) == 0 ) { MathScript *scr = new MathScript(node); unsigned int key = csHashCompute(scr->name); scripts.Put(key,scr); } } } MathScriptEngine::~MathScriptEngine() { csHash::GlobalIterator it (scripts.GetIterator ()); while (it.HasNext ()) { MathScript* script = it.Next (); delete script; } scripts.DeleteAll(); } MathScript *MathScriptEngine::FindScript(const char *name) { unsigned int key = csHashCompute(name); csHash::Iterator iter = scripts.GetIterator(key); while (iter.HasNext()) { MathScript *ptr = iter.Next(); if (ptr->name == name) return ptr; } return NULL; } csRandomGen MathScriptEngine::rng; double MathScriptEngine::RandomGen(const double *dummy) { return MathScriptEngine::rng.Get(); } double MathScriptEngine::Power(const double *parms) { return pow(parms[0],parms[1]); } double MathScriptEngine::GetSkillValue(const double *parms) { psCharacter *my_char = (psCharacter *)(intptr_t)parms[0]; PSSKILL skill = (PSSKILL)(int)parms[1]; double value = my_char->GetSkills()->GetSkillRank(skill); // always give a small % of melee (unharmed) skill if (skill==PSSKILL_MARTIALARTS && value==0) value = 0.2; return value; } double MathScriptEngine::GetAverageSkillValue(const double *parms) { psCharacter *my_char = (psCharacter *)(intptr_t)parms[0]; PSSKILL skill1 = (PSSKILL)(int)parms[1]; PSSKILL skill2 = (PSSKILL)(int)parms[2]; PSSKILL skill3 = (PSSKILL)(int)parms[3]; double v1 = my_char->GetSkills()->GetSkillRank(skill1); if (skill2!=PSSKILL_NONE) { double v2 = my_char->GetSkills()->GetSkillRank(skill2); v1 = (v1+v2)/2; } if (skill3!=PSSKILL_NONE) { double v3 = my_char->GetSkills()->GetSkillRank(skill3); v1 = (v1+v3)/2; } // always give a small % of combat skill, or players will never be able to get the first exp if (v1==0) v1 = 0.7; return v1; } double MathScriptEngine::GetStatValue(const double *parms) { psCharacter *my_char = (psCharacter *)(intptr_t)parms[0]; PSITEMSTATS_STAT stat = (PSITEMSTATS_STAT)(int)parms[1]; return (double)my_char->GetAttributes()->GetStat(stat); } double MathScriptEngine::GetArmorVSWeaponResistance(const double *parms) { psItem *weapon = (psItem *)(intptr_t)parms[0]; psItem *armor = (psItem *)(intptr_t)parms[1]; // if no armor return 1 if (!armor) return 1.0F; return weapon->GetArmorVSWeaponResistance(armor->GetCurrentStats()); }