• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Spell Advanced Shadow Clones

tetra20

DD
Joined
Jan 17, 2009
Messages
1,315
Solutions
4
Reaction score
323
Location
Egypt
Hello Everyone,This is a spell that i have scripted.. it is good for some use but will need edits as it might somehow fuck up at any moment xD

Tested only on 0.3.7
but should work fine for 0.4

i dont script for tfs 1.2 unless there is a reason :3 :oops::oops:

Starting By Screenshots:

ba1f0c064ef0e909f6c5f363f4205c8a.png

52fa1674453ea09d2a4f0a34588a85b7.gif


What so Special About This Script:

1-It Can have spells,your shadow clones will use your spells.. best 2-3 spells you can increase/decrease it
2-it uses your armor/Defense of shield and gear
3-It uses your weapon attack to precisely be like yourself
4-The monster have the same hp/skills/name just with an extra black skull.. the clone wont target you
5-The Clone can heal too and healing depends on your hp.. same for spells.. depends on your dmg and such

Now Time For the Scripts

Firs
t Source Edits:

Luascript.cpp:

add This
Code:
int32_t LuaInterface::luaDoCreateCustomMonster(lua_State* L)
{
   //doCreateCustomMonster(name, pos, outfit, health, attspell,defensespell,defense,armor,baseSpeed,exp,skull)
   // created By MeNi for otland.net //
   ScriptEnviroment* env = getEnv();
   MonsterType* pobranyTyp = new MonsterType();
   Monster* monster;
   int64_t health, defense, armor, baseSpeed,experience;
   Outfit_t outfit;
   Skulls_t skull;
   PositionEx pos;

   skull = (Skulls_t)popNumber(L);
   experience = popNumber(L);
   baseSpeed = popNumber(L);
   armor = popNumber(L);
   defense = popNumber(L);
   std::string defensespells = popString(L);
   std::string attackspells = popString(L);
   health = popNumber(L);
   outfit = popOutfit(L);
   popPosition(L, pos);
   std::string name = popString(L);

   pobranyTyp->spellAttackList.clear();

   pobranyTyp->health = health;
   pobranyTyp->healthMax = health;
   pobranyTyp->outfit = outfit;
   pobranyTyp->name = name;
   pobranyTyp->nameDescription = name;
   pobranyTyp->lookCorpse = 0;
   pobranyTyp->targetDistance = 1;
   pobranyTyp->experience = experience;
   pobranyTyp->isSummonable = false;
   pobranyTyp->isIllusionable = false;
   pobranyTyp->isConvinceable = false;
   pobranyTyp->isWalkable = true;
   pobranyTyp->pushable = false;
   pobranyTyp->isAttackable = true;
   pobranyTyp->isHostile = true;
   pobranyTyp->isConvinceable = true;
   pobranyTyp->canPushItems = true;
   pobranyTyp->canPushCreatures = true;
   pobranyTyp->conditionImmunities |= CONDITION_PARALYZE;
   pobranyTyp->conditionImmunities |= CONDITION_DRUNK;
   pobranyTyp->conditionImmunities |= CONDITION_INVISIBLE;
   pobranyTyp->defense = defense;
   pobranyTyp->armor = armor;
   pobranyTyp->skull = skull;
   pobranyTyp->baseSpeed = baseSpeed;
   pobranyTyp->changeTargetSpeed = 0;
   pobranyTyp->changeTargetChance = 0;
   if (!attackspells.empty()){
     xmlNodePtr root_attack = xmlDocGetRootElement(xmlParseMemory(attackspells.c_str(), attackspells.length()));
     xmlNodePtr tmpNode_attack = root_attack->children;
     while (tmpNode_attack)
     {
       if (!xmlStrcmp(tmpNode_attack->name, (const xmlChar*)"attack"))
       {
         spellBlock_t sb;
         if (g_monsters.deserializeSpell(tmpNode_attack, sb, "doCreateCustomMonster"))
           pobranyTyp->spellAttackList.push_back(sb);
       }
       tmpNode_attack = tmpNode_attack->next;
     }
   }
   if (!defensespells.empty()){
     xmlNodePtr root_defense = xmlDocGetRootElement(xmlParseMemory(defensespells.c_str(), defensespells.length()));
     xmlNodePtr tmpNode_defense = root_defense->children;
     while (tmpNode_defense)
     {
       if (!xmlStrcmp(tmpNode_defense->name, (const xmlChar*)"defense"))
       {
         spellBlock_t defense;
         if (g_monsters.deserializeSpell(tmpNode_defense, defense, "doCreateCustomMonster"))
           pobranyTyp->spellDefenseList.push_back(defense);
       }
       tmpNode_defense = tmpNode_defense->next;
     }
   }
   monster = Monster::createMonster(pobranyTyp);

   if (!g_game.placeCreature(monster, pos, false, false))
   {
     delete monster;
     lua_pushboolean(L, false);
     return 1;
   }

   lua_pushnumber(L, env->addThing((Thing*)monster));
   return 1;
}

Code:
//doCreateCustomMonster(....)
   lua_register(m_luaState, "doCreateCustomMonster", LuaInterface::luaDoCreateCustomMonster);

go to monster.h
Add This
Code:
bool isClone(Creature* creature) { return creature && getName() == creature->getName() && getMonster() && getSkull() == SKULL_BLACK; }

Now monster.cpp

Replace
Code:
isTarget(Creature* creature)

The Whole Function with this
Code:
bool Monster::isTarget(Creature* creature)
{
   return (!isClone(creature) && !creature->isRemoved() && creature->isAttackable() && creature->getZone() != ZONE_PROTECTION
     && canSeeCreature(creature) && creature->getPosition().z == getPosition().z) ;
}

creature.cpp

add This
Code:
  if (creature && getName() == creature->getName() && getSkull() == SKULL_BLACK && getMonster())
     return false;

Under
Code:
  if(creature)
   {
     const Position& creaturePos = creature->getPosition();
     if(creaturePos.z != getPosition().z || !canSee(creaturePos))
     {
       attackedCreature = NULL;
       return false;
     }
   }

This should ensure the monsters wont attack you.. the shadow clones

Now lua part:
Code:
local config = {
   HpBuff = 2.8, -- HpBuff * your max health
   AttacksBuff = 3.0, -- att Buff * your Attack
   DefenseBuff = 2.4, -- defense Buff * your defense
   ArmorBuff = 2.1, -- same
   SkillsBuff = 2.5, -- same
   range = {5,5}, -- max x,y of range
   blockedSpell = {'Light Healing','Shadow Clones','revive','Find Person','Light','Magic Rope','Cure Poison','Wound Cleansing'}, -- spells blocked in attack.. loop will jump it and goto next one
   exhaustionTime = 300, -- time needed to recast spell
   maxSpells = 10, -- max numbers of spells that boss can use
   distance = 5, -- distance between you and player
   maxChance = 25, -- chance for spells.. it is random 1-25
   maxNumberOfCreatures = 4 -- max number of clones.
}
--[[ it is only 1 clone per creature..
]]
function getDef(cid)
   local defense = 0
   for i = CONST_SLOT_RIGHT,CONST_SLOT_LEFT do
     if getPlayerSlotItem(cid, i).uid > 0  then
       if getItemInfo(getPlayerSlotItem(cid,i).itemid).defense then
         defense = defense + getItemInfo(getPlayerSlotItem(cid,i).itemid).defense
       end
     end
     if getPlayerSlotItem(cid, i).uid > 0 then
       if getItemInfo(getPlayerSlotItem(cid, i).itemid).extraDefense > 0 then
         defense = defense + getItemInfo(getPlayerSlotItem(cid, i).itemid).extraDefense
       end
     end
   end
   return defense + getPlayerSkillLevel(cid, SKILL_SHIELD)
end

function getArm(cid)
   local armor = 0
   for i = CONST_SLOT_FIRST,CONST_SLOT_LAST do
     if getPlayerSlotItem(cid, i).uid > 0 then
       if getItemInfo(getPlayerSlotItem(cid,i).itemid).armor > 0 then
         armor = armor + getItemInfo(getPlayerSlotItem(cid,i).itemid).armor
       end
     end
   end
   return armor > 0 and armor or 1
end

function getAtk(cid)
   local weapon = getPlayerWeapon(cid)
   local attack = 1
   if weapon.uid > 0 then
     if getItemInfo(weapon.itemid).attack then
       attack = attack + getItemInfo(weapon.itemid).attack
     end
     if getItemInfo(weapon.itemid).extraAttack then
       attack = attack + getItemInfo(weapon.itemid).extraAttack
     end
   end
   return config.AttacksBuff*attack
end

function getSkill(cid)
   if isSorcerer(cid) or isDruid(cid) then
     return getPlayerMagLevel(cid) > 0 and getPlayerMagLevel(cid) or 1
   elseif isPaladin(cid) then
     return getPlayerSkillLevel(cid, SKILL_DISTANCE)
   elseif isKnight(cid) then
     local skills = {getPlayerSkillLevel(cid,SKILL_CLUB),getPlayerSkillLevel(cid,SKILL_SWORD),getPlayerSkillLevel(cid,SKILL_AXE)}
     return getPlayerSkillLevel(cid, getHighestOfArray(skills))
   else
     return getPlayerSkillLevel(cid, SKILL_FIST)
   end
   return 1
end

function getHighestOfArray(ar)
   table.sort(ar)
   return ar[#ar]
end
  
function getStrongestSpells(cid, maximum)
   local count = getPlayerInstantSpellCount(cid)
  local t = {}
  for i = 0, count - 1 do
  local spell = getPlayerInstantSpellInfo(cid, i)
  table.insert(t, spell)
  end
  table.sort(t, function(a, b) return a.level < b.level end)
   local spellsNames = {}
   local counter = 0
   local i = 0
   while(counter < maximum) do
     if not isInArray(config.blockedSpell,t[#t-i].name) then
       table.insert(spellsNames,t[#t-i].name)
       counter = counter + 1
     end
     i = i + 1
     if((#t - i) <= 0)then
       break
     end
   end
   return spellsNames
end
--Dont Touch this
function setAttack(cid)
   math.randomseed(os.time())
   local attacks = "<a>"
   attacks = attacks .. '<attack name="melee" interval="1000" chance="100" skill="'..config.SkillsBuff*getSkill(cid)..'" attack="'..getAtk(cid)..'" radius="1" target="1"/>'
   local spells = getStrongestSpells(cid, config.maxSpells)
   if(#spells > 0)then
     for i = 1,#spells do
       local words = '<attack name="replaceThis" interval="'..math.random(1000,8000)..'" chance="'..math.random(1,config.maxChance)..'"/>'
       local text = string.gsub(words,"replaceThis",spells[i])
       attacks = attacks .. text
     end
   end
   attacks = attacks .. "</a>"
   return attacks
end
--Dont Touch this
function setDefense(c,s)
   local defenses = "<s>"
   for i=1,#c do
     defenses = defenses .. c[i].text
   end
   defenses = defenses .. "</s>"
   return defenses
end

function summonClones(cid, center,targetPlayer)    
   local sp = {
     defenses = {
       [1] = {text = '<defense name="healing" interval="1000" chance="'..math.random(8,20)..'" min="'..(math.floor(getCreatureMaxHealth(cid) * 0.08))..'" max="'..(math.floor(getCreatureMaxHealth(cid) * 0.15))..'"><attribute key="areaEffect" value="blueshimmer"/></defense>'},
     }
   }
   doSendMagicEffect(center,CONST_ME_TELEPORT)
   local monster = doCreateCustomMonster(getCreatureName(cid),center,getCreatureOutfit(cid),config.HpBuff*getCreatureMaxHealth(cid),setAttack(cid),setDefense(sp.defenses,c),config.DefenseBuff*getDef(cid),config.ArmorBuff*getArm(cid),getCreatureSpeed(cid),0, SKULL_BLACK)
   doMonsterSetTarget(monster, targetPlayer)
   return true
end

function onCastSpell(cid, var)
   if exhaustion.check(cid, 50062) then
     doPlayerSendCancel(cid, "You Are Still Collecting Your Power.. Time Left:"..(math.floor(exhaustion.get(cid,50062) / 60) >= 1 and ""..math.floor(exhaustion.get(cid,50062) / 60).." Minutes." or ""..math.floor((exhaustion.get(cid,50062) % 60)).." Seconds.")..".")
     doSendMagicEffect(getThingPos(cid),CONST_ME_POFF)
     return false
   end
  
   local spec = getSpectators(getThingPos(cid), config.range[1], config.range[2])
   if #spec > 0 then
     for i = 1,math.min(config.maxNumberOfCreatures+1,#spec) do
       if getDistanceBetween(getThingPos(cid), getThingPos(spec[i])) <= config.distance and getCreatureName(spec[i]) ~= getCreatureName(cid)
       and isSightClear(getThingPos(cid), getThingPos(spec[i]), false) and not isNpc(spec[i]) and not getTileInfo(getThingPos(spec[i])).protection then
         summonClones(cid, getClosestFreeTile(cid, getThingPos(spec[i])),spec[i])
         doSendDistanceShoot(getThingPos(cid), getThingPos(spec[i]), math.random(1,40))
       end
     end
     exhaustion.set(cid, 50062, config.exhaustionTime)
   end
   return true
end

I Dont Support This Script / Fix it.. you are on your own.. you have to think of few things also for tip

there is a missing part in luascript.h but you can add it yourself.. it is easy

Healing is default and monster will heal randomly of 8% - 15%

Shadow Clones summons automatically with a target

Blocked Spell Uses name not word..
 
Last edited:
Code:
static int32_t luaDoCreateCustomMonster(lua_State* L);
-Fixed Few bugs if Caster doesn't have weapon or Caster has 0 vocation or Caster has no Ml
-Fixed infinite loop if the counter < maximum and while loop finished the spells list
-Fixed if the spells is = 0,it will ignore attacks , just melee
 
Last edited:
Code:
static int32_t luaDoCreateCustomMonster(lua_State* L);
-Fixed Few bugs if Caster doesn't have weapon or Caster has 0 vocation or Caster has no Ml
-Fixed infinite loop if the counter < maximum and while loop finished the spells list
-Fixed if the spells is = 0,it will ignore attacks , just melee
That's the missing part?
 
i dont script for tfs 1.2 unless there is a reason :3 :oops::oops:
in TFS 1.2 you can do the spell in Lua without having to do source edits. (its the reason why i use TFS 1.x, maybe it will be same for you)
 
in TFS 1.2 you can do the spell in Lua without having to do source edits. (its the reason why i use TFS 1.x, maybe it will be same for you)
I can do C++ thats why i dont use tfs 1.x Also Source edit is critically needed here for the custom function i suppose
 
can someone help me?
ASDASDAS.png


my luascript.cpp
http://wklej.org/id/2780079/

my monsters.h
http://wklej.org/id/2780078/
monster.h

Copy deserializeSpell From Private to public
Code:
bool deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::string& description = "");
Move it to above
Code:
private:
to be like this
Code:
        bool deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::string& description = "");
    private:
 
blad2.png

last problem :(

I've find problem
I just needed to change

//doRemoveItem(uid[, count])
lua_register(m_luaState, "doRemoveItem", LuaScriptInterface::luaDoRemoveItem);

to
//doRemoveItem(uid[, count])
lua_register(m_luaState, "doRemoveItem", LuaScriptInterface::luaDoPlayerRemoveItem);
 
Last edited:
Back
Top