Developer Store
Support
Member Forums

Screenshots
FAQ
Documentation
License
Known Issues
Downloads

MMOWorkshop.com Store Opened!
Torque MMO Kit - Open Sourced!
Torque MMO Kit - 1.5.2 Port Alpha Test
Torque MMO Kit - OSX Status

GarageGames.com irc.prairiegames.com
#mmoworkshop

PyTorque
TGB Web Browser


karvermimic

hallsofvalhalla - After a long epiphany
Leathel - FoHO pre-Alpha 2.42
OldRod - More Musings on the MMO Industry
xapken - nice
J.C. Smith - 0.0.4.1 Build Notes
Wolf Dreamer - Pointless blog of pointless things
AthlonJedi2 - Server Nuked !!!!!
gamer_goof - New character model GIRL1 available only $4
... MORE BLOGS!

My other project
A Message to all the new people.
MyGame
changing the primary class
See Ya Classes, Hello Skills!
XP Based Skills
fxFoliageReplicator zone loading...
Is it just me, or is it slow in ...
Places where NPC quest-givers ha...
TalentRaspel Grid

Introduction

This is another snippet provided by Xerves. This again is a very large sweeping change over many files (time you are done it will be over 20 files) so I recommend you backup everything and do this in one step so you can identify any issues that might come after installation. Check my Rafermand - Xerves posting for any issues with this and they will be updated into this site once they are found and fixed.

For those curious this will allow spells and spells to have formulas that will affect most effects (stats, damage, Dots, drains, etc), range formulas (regular and AOE) and mana consumption. These values are passed to a function that is used to parse the values and after that can be used for any variety of things. What comes with this code is very simple, level of caster and some stats. You could write macro functions or whatever you want into the function for a ton of options. I have a redefined system that has points/mastery for skills/spells so I use that for a lot of calculation for skills and spells so this is why this is handy. Also, it can get rid of those confusing 500 damage kick skills that do 20 damage and instead factor it on level and other stats if you want (there is an option to disable this in the instructions, so be on the look for that).

Also added is the rending ability in Information screen to show this data so as you gain levels and stats the damage or whatever is tied to a formula will update. For the stock system spells are the only thing that will work here, but with the modifications mentioned earlier you will be aboe to see skills to. Even without the modifications you can still use the formulas for skills, you just won't see them in real time.

Unfortunately, this modification will screw up how the encyclopedia looks due to the nature that a formula is now there instead of a static value. All encyclopedia files will be reworked in the instructions for a change and some formatting pointers to make it more readable after the change.

Well that is a long introduction, on to the instructions.

WARNING

Please be aware that this is a sweeping change that will change some infrastructre points of your code. Future patches or migrations might need to be done manually after doing this since some Spell functions are changed and new paramaters are added. Also minReuseTime and maxReuseTime has been removed and need to be replaced with reuseTime for all skills.

Values for skills/spells affected by this change

skills
-------------
minReuseTime (deleted)
maxReuseTime (deleted)
reuseTime (replacement)

spells
-------------
castTime
recastTime
duration
castRange
aoeRange
manaCost

Effects
-------------
Damage
Leech
Drain
Regen
Any Stat added through AddStat (HP/Str/Invis/etc)

Items
-------------
Obj is now passed through the whole spell process.  You could write some code in the function listed below in 
core.py to make use of the object if you wanted.  Beyond that it doesn't factor in.

mud/world/core.py

Add the function that is used to parse the formulas.

#Passes all sorts of useable information.  All spells, skills, ghost (information),and item usage is passed through here.  
#Items are not being used currently, but you can pull information from the item instance or index if you want to
def ParseStatValue(value, src, skillname,ghost,proto,item):
    #friendly text as used in the spell/skill is first part, actual value in ghost or src is second value
    ghostkeys = {'str':'STR', 'dex':'DEX', 'bdy':'BDY', 'mnd':'MND', 'wis':'WIS', 'agi':'AGI', 'ref':'REF', 'mys':'MYS', 'pre':'PRE', 'plevel':'PLEVEL'}
    srckeys = {'str':'str', 'dex':'dex', 'bdy':'bdy', 'mnd':'mnd', 'wis':'wis', 'agi':'agi', 'ref':'ref', 'mys':'mys', 'pre':'pre', 'level':'plevel'}
    stats = {}

    if not src and not ghost:
        print "ParseStatValue: Invalid src and ghost passed"
        return 1
    
    #save some overhead, lets not do any of this if we don't need to.  Any computed value that isn't passed as a string should get cought here    
    if value.isdigit():
        value = eval(value)
        return float(value)
     
    origvalue = value   
    #Ghost might not be fully populated yet...
    if ghost and not ghost.CINFO:
        return 1
    #populate a stats keyed array 
    if ghost and ghost.CINFO:
        for k,attr in ghostkeys.iteritems():
            stats[k] = ghost.CINFO[attr]
    if src:
        for k,attr in srckeys.iteritems():
            stats[k] = getattr(src,attr)
                        
    #Catch these values.  You might want to use just Second and Minute for easier readability  
    if value.find("durSecond"):     
        value = value.replace("durSecond",'%d'%durSecond)
    if value.find("durMinute"):     
        value = value.replace("durMinute",'%d'%durMinute)
    #Check for skillPoints
    if (skillname and src):
        if value.find("skillPoints"):
            if src.skillPoints.get(skillname):
                value = value.replace("skillPoints",'%d'%src.skillPoints.get(skillname))
            else:
                value = value.replace("skillPoints",'0')
            
    #iterate through the keyed array listed near the top and spit out values
    if stats:
        for k,attr in stats.iteritems():
            if value.find(k):
                value = value.replace(k,'%d'%attr)
    
    try:              
        value = eval(value)
        return float(value)
    except:
        print "Error: ParseStatValue: %s is invalid...parsed string is %s"%(origvalue, value)
        return 1

mud/world/shared/playdata.py

In class CharSpell? Info find the following

        state['ID']=proto.id
        state['SLOT']=self.charSpell.slot
        state['RECASTTIMER']=0
        state['SPELLINFO']=SpellInfo(proto,self.charSpell.level)

Change the last line (SPELLINFO) to look like this

        state['SPELLINFO']=SpellInfo(proto,self.charSpell,self.char)

A few lines down find the following

    def refresh(self):
        state = self.state
        if state == None:
            return
        changed = {}

Immediately after that add the following

        #refreshes the spell data since it isn't always static....this will introduce more bandwidth of course.
        state['SPELLINFO'].fullRefresh()

A few lines below find the following class

#this is used for "Character spells" which need to be split out... so we can use this for vendor... yargh
class SpellInfo(pb.Cacheable):

Before it add the following

#attributes go in here and then it is pulled out via key
ghostkeys = {'STR':'str', 'DEX':'dex', 'BDY':'bdy', 'MND':'mnd', 'WIS':'wis', 'AGI':'agi', 'REF':'ref', 'MYS':'mys', 'PRE':'pre', 'PLEVEL':'plevel'}

Find the following code

class SpellInfo(pb.Cacheable):
    def __init__(self,proto,level=0):
        self.observers = []
        self.proto = proto
        self.level = level
        
    def stoppedObserving(self, perspective, observer):
        #if observer in self.observers:
        self.observers.remove(observer)

    def getFullState(self):
        state = self.state = {}
        
        state['ID'] = self.proto.id
        state['LEVEL'] = self.level #this is a character spell info if > 0
        
        return state

Replace it with this

class SpellInfo(pb.Cacheable):
    def __init__(self,proto,charSpell=None,character=None):
        self.observers = []
        self.proto = proto
        self.charSpell = charSpell
        self.character = character
        
    def stoppedObserving(self, perspective, observer):
        #if observer in self.observers:
        self.observers.remove(observer)

    def getFullState(self):
        state = self.state = {}
        character = self.character
        mob = character.mob
        spawn = mob.spawn
        
        state['ID'] = self.proto.id
        state['LEVEL'] = self.charSpell.level #this is a character spell info if > 0
        #This is very hackish, I will fix this sometime in the future...it gets some basic information about the caster/user
        state['CINFO'] = {}
        for k,attr in ghostkeys.iteritems():
            state['CINFO'][k] = getattr(mob,attr)
         return state

Find the following

class SpellInfoGhost(pb.RemoteCache):
    def __init__(self,spID = None):
        self.text = ""
        self.ID = spID

After it add this:

        self.CINFO = None

A few lines down find this

        self.NEGATE = negate
        self.NEGATEMAXLEVEL = negatemax

Add the following after it

        ghost = self

If you are using my skill code you will need to make the following modifcations in this function: def get_spell_info_window(ghost, isSpell):

If not you need to make the changes in: class SpellInfoGhost?(pb.RemoteCache?):

Find the following:

        # Range
        if self.TARGET != RPG_TARGET_SELF:
            text.append(r'\c3Range: \c0%im '%self.CASTRANGE)

Change it to

        # Range
        if self.TARGET != RPG_TARGET_SELF:
            text.append(r'\c3Range: \c0%im '%ParseStatValue(ghost.CASTRANGE,None,"",ghost,None,None))

Find the following:

        # AOE Range
        if self.AOERANGE:
            text.append(r'\c3AoE Range: \c0%im '%self.AOERANGE)

Change it to

        # AOE Range
        if self.AOERANGE:
            text.append(r'\c3AoE Range: \c0%im '%ParseStatValue(ghost.AOERANGE,None,"",ghost,None,None))

Find the following:

            d = self.DURATION/6

Change it to

            d = ParseStatValue(ghost.DURATION,None,"",ghost,None,None)/6

Find the following:

            d = self.CASTTIME/6

Change it to

            d = ParseStatValue(ghost.CASTTIME,None,"",ghost,None,None)/6

Find the following:

        # Mana Cost
        text.append(r'\c3Mana: \c0%i '%self.MANACOST)

Change it to:

        # Mana Cost
        text.append(r'\c3Mana: \c0%i '%ParseStatValue(ghost.MANACOST,None,"",ghost,None,None))

mud/world/effect.py

Find the class EffectDamage? and change the amount value to look like this:

    amount = StringCol(default='1')

In EffectStat? make the same change to the value field

    value = StringCol()

In EffectLeech? do the same thing with the following field

    leechTick = StringCol(default="")
    leechTickRate = StringCol(default="")

In EffectDrain? do the same thing with the following field

    drainTick = StringCol(default="")
    drainTickRate = StringCol(default="")

In EffectRegen? do the same thing with the following field

    regenTick = StringCol(default="")
    regenTickRate = StringCol(default="")

Replace these following 3 functions with the ones here

#A bit of more math added here since floating values could happen, so ceil and floor is called when needed.  Make sure to check this code since
#Damage could of changed in future revisions of the kit and the parameters could be incorrect
def DoDrain(effect):
    proto = effect.effectProto
    drain = proto.drainEffect
    if drain:
        effect.drainTimer -= 3
        tickrate = math.ceil(ParseStatValue(drain.drainTickRate,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        tick = math.floor(ParseStatValue(drain.drainTick,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        if effect.drainTimer <= 0:
            dst = effect.dst
            effect.drainTimer = tickrate
            if drain.drainType == "health":
                Damage(dst,effect.src,tick*effect.mod,DAMAGEFORRESIST[proto.resist],None,False,False,True)
            elif drain.drainType == "mana":
                dst.mana -= tick*effect.mod
                if dst.mana < 0:
                    dst.mana = 0
            elif drain.drainType == "stamina":
                dst.stamina -= tick*effect.mod
                if dst.stamina < 0:
                    dst.stamina = 0


def DoRegen(effect):
    proto = effect.effectProto
    regen = proto.regenEffect
    if regen:
        effect.regenTimer -= 3
        tickrate = math.ceil(ParseStatValue(regen.regenTickRate,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        tick = math.floor(ParseStatValue(regen.regenTick,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        if effect.regenTimer <= 0:
            dst = effect.dst
            effect.regenTimer = tickrate
            if regen.regenType == "health":
                dst.health += tick*effect.mod
                if dst.health > dst.maxHealth:
                    dst.health = dst.maxHealth
            elif regen.regenType == "mana":
                dst.mana += tick*effect.mod
                if dst.mana > dst.maxMana:
                    dst.mana = dst.maxMana
            elif regen.regenType == "stamina":
                dst.stamina += tick*effect.mod
                if dst.stamina > dst.maxStamina:
                    dst.stamina = dst.maxStamina


def DoLeech(effect):
    proto = effect.effectProto
    leech = proto.leechEffect
    if not leech:
        return
    effect.leechTimer -= RPG_SET_ZONECOMBAT_TICK
    if effect.leechTimer <= 3:
        src = effect.src
        tickrate = math.ceil(ParseStatValue(leech.leechTickRate,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        tick = math.floor(ParseStatValue(leech.leechTick,effect.src,effect.parent.skill,None,effect.parent.spellProto,effect.parent.spitem))
        effect.leechTimer = tickrate
        if leech.leechType == 'health':
            src.health += tick*effect.mod
            if src.health > src.maxHealth:
                src.health = src.maxHealth
        elif leech.leechType == 'mana':
            src.mana += tick*effect.mod
            if src.mana > src.maxMana:
                src.mana = src.maxMana
        elif leech.leechType == 'stamina':
            src.stamina += tick*effect.mod
            if src.stamina > src.maxStamina:
                src.stamina = src.maxStamina

In class Effect find the following

             for dmg in proto.damage:
                 
                 if dmg.stage == RPG_EFFECT_STAGE_GLOBAL:
                    value = dmg.amount*self.mod

Replace the last line with the following 2

                    value = ParseStatValue(dmg.amount,self.src,self.parent.skill,None,self.parent.spellProto,self.parent.spitem)
                    value = value*self.mod

About 60 lines down find the following

                         src.casting.cancel()
                         if src.player:
                             src.player.sendGameText(RPG_MSG_GAME_DENIED,"%s's casting has been interrupted!\\n"%(src.name))

                value = st.value*self.mod

Replace the last line with the following 2

                value = ParseStatValue(st.value,self.src,self.parent.skill,None,self.parent.spellProto,self.parent.spitem)
                value = value*self.mod

Same thing, find the following lines

         if self.time == 0:
             for dmg in proto.damage:
                 if dmg.stage == RPG_EFFECT_STAGE_BEGIN:
                    value = dmg.amount * self.mod

Replace value with this

                    value = ParseStatValue(dmg.amount,self.src,self.parent.skill,None,self.parent.spellProto,self.parent.spitem)
                    value = value * self.mod

Just a few lines below find this

                     Damage(dst,src,value,dmg.type)
             
             for st in proto.stats:
                 if st.stage == RPG_EFFECT_STAGE_BEGIN:
                    value = st.value * self.mod

Replace value with this

                    value = ParseStatValue(st.value,self.src,self.parent.skill,None,self.parent.spellProto,self.parent.spitem)
                    value = value * self.mod

mud/world/crafting.py

Find the following lines

            if slevel < skillReq and randint(0,int((1.0 - float(slevel)/float(skillReq)) * 10.0)):
                player.sendGameText(RPG_MSG_GAME_DENIED,"%s failed the focus enchantment.\\n"%char.name)
                # linearly decrease drain with higher skill level
                buffMod = 2.0 - float(slevel) / 1000.0
                mob.processesPending.append(Spell(mob,mob,SpellProto.byName("Enchanting - Mana drain"),buffMod,0,True))

Replace that last line with this

                mob.processesPending.append(Spell(mob,mob,SpellProto.byName("Enchanting - Mana drain"),buffMod,0,"",None,True))

mud/world/item.py

Find the following lines

        if proto.projectile:
            p = Projectile(mob,mob.target)
            p.spellProto = proto
            p.launch()
        else:
            SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod)

Replace the last one with this

            SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod,"",self)

mud/world/mobspells.py

Find the following lines

    def considerHealing(self):
        mob = self.mob
        zone = mob.zone
        simAvatar = zone.simAvatar
        
        found = False
        for proto in self.healing:
            if mob.mana >= proto.manaCost:

Replace the last line with the following

            if mob.mana < ParseStatValue(proto.manaCost,mob,"",None,proto,None):

A handful of lines down find the following

                    if len(self.slow) and mob.target.slow <=0 and not random.randint(0,2):
                        if len(self.slow)==1:
                            proto = self.slow[0]
                        else:
                            proto = self.slow[random.randint(0,len(self.slow)-1)]
                    else:
                        proto = self.harmful[random.randint(0,len(self.harmful)-1)]
            
            if mob.mana < proto.manaCost:

Replace that last line with this

            if mob.mana < ParseStatValue(proto.manaCost,mob,"",None,proto,None):

A few more lines down find the following

                if proto.target == RPG_TARGET_OTHER:
                    if len(pmobs) == 1:
                        mob.spellTarget = pmobs[0]
                    else:
                        mob.spellTarget = pmobs[random.randint(0,len(pmobs)-1)]
                
                
            if mob.mana < proto.manaCost:

Again replace the last line

            if mob.mana < ParseStatValue(proto.manaCost,mob,"",None,proto,None):

mud/world/combat.py

Find the following block of code

            if item and item.skill == "Fists":
                for ispell in item.spells:
                    if ispell.trigger == RPG_ITEM_TRIGGER_MELEE:
                        if ispell.frequency <= 1 or not randint(0,ispell.frequency):
                            proto = ispell.spellProto
                        
                            tgt = defender
                            if proto.target == RPG_TARGET_SELF:
                                tgt = attacker
                            if proto.target == RPG_TARGET_PARTY:
                                tgt = attacker
                            if proto.target == RPG_TARGET_ALLIANCE:
                                tgt = attacker
                            if proto.target == RPG_TARGET_PET:
                                tgt = attacker.pet
                                
                            if tgt:
                                SpawnSpell(proto,attacker,tgt,tgt.simObject.position,1.0)

Last line replace with this

                                SpawnSpell(proto,attacker,tgt,tgt.simObject.position,1.0,"",item)

A hanful of lines down find the following

        damagedProcs = defender.damagedProcs
        damagedProcs.extend(defender.itemSetSpells.get(RPG_ITEM_TRIGGER_DAMAGED, []))
        if actualdmg and len(damagedProcs):# and not randint(0,2):
            for item in damagedProcs:
                for ispell in item.spells:
                    if ispell.trigger == RPG_ITEM_TRIGGER_DAMAGED:
                        if ispell.frequency <= 1 or not randint(0,ispell.frequency):
                            proto = ispell.spellProto
                        
                            tgt = attacker
                            if proto.target == RPG_TARGET_SELF:
                                tgt = defender
                            if proto.target == RPG_TARGET_PARTY:
                                tgt = defender
                            if proto.target == RPG_TARGET_ALLIANCE:
                                tgt = defender
                            if proto.target == RPG_TARGET_PET:
                                tgt = defender.pet
                                
                            if tgt:
                                SpawnSpell(proto,defender,tgt,tgt.simObject.position,1.0)

Replace that last line with

                                SpawnSpell(proto,defender,tgt,tgt.simObject.position,1.0,"",item)

Some more lines down find the following

                            if tgt:
                                if ispell.trigger == RPG_ITEM_TRIGGER_POISON:
                                    wpn.procs[ispell][0] -= 1
                                    if wpn.procs[ispell][0] <= 0:
                                        del wpn.procs[ispell]
                                        if attacker.player:
                                            attacker.player.sendGameText(RPG_MSG_GAME_SPELLEND,"The %s on %s's %s has worn off.\\n"%(ispell.spellProto.name,attacker.name,wpn.name))
                                        # only need to update for description,
                                        # so only do if attacker is a player or a player pet
                                        if attacker.player or (attacker.master and attacker.master.player):
                                            refreshProcs = True
                                
                                SpawnSpell(proto,attacker,tgt,tgt.simObject.position,1.0)

Change that last line to

                                SpawnSpell(proto,attacker,tgt,tgt.simObject.position,1.0,"",wpn)

mud/world/mob.py

Find the following lines

        #player -> time
        self.interactTimes = {}
        
        #persistent spells
        if self.character:
            for store in self.character.spellStore:
                if not self.character.dead:
                    self.processesPending.append(Spell(self,self,store.spellProto,store.mod,store.time,False,False,True,store.level))

Replace the last line with the following

                    self.processesPending.append(Spell(self,self,store.spellProto,store.mod,store.time,"",None,False,True,store.level))

Find the following lines

                if charskills != None and cskill.trained and self.player and not self.player.monster:
                    if not charskills.has_key(cskill.skillname):
                        continue
                
                maxValue = cskill.getMaxValueForLevel(level)
                reuseTime = cskill.getReuseTimeForLevel(level)

Replace the last line with this

                reuseTime = cskill.getReuseTimeForLevel(level,self)

2 or 3 lines down find the following

                # don't actually qualify in this class, though charskills has value or something
                if cskill.minReuseTime or cskill.maxReuseTime:

Replace the last line with this

                if reuseTime:

mud/world/skill.py

Find the following lines

    skillname = StringCol(default = "")
    levelGained = IntCol(default = 0)
    levelCapped = IntCol(default = 0)
    minReuseTime = IntCol(default = 0)
    maxReuseTime = IntCol(default = 0)

Remove MinReuseTime? and maxReuseTime (Delete them) and put this in instead

    reuseTime = StringCol(default = "")

Replace the function getReuseTimeForLevel (including the definition) with the following

    def getReuseTimeForLevel(self,level,mob):
        if not self.reuseTime:
            return 0
        if not self.maxValue:
            return 0        
        if level < self.levelGained:
            return 0   

        return int(ParseStatValue(self.reuseTime, mob, self.skillname,None,None,None))

This is OPTIONAL. This will remove the sliding modifier on a skill based on the level. If you remove this you can specify your own values for level, etc. To change it find the following code.

def DoSkillSpell(mob,skillname):
    from projectile import Projectile
    from spell import SpawnSpell
    
    mskill = mob.mobSkillProfiles[skillname]
    cskill = mskill.classSkill
    
    mv = mob.skillLevels.get(skillname,0)
    if not mv:
        return
    
    mod = float(mv)/float(cskill.maxValue)
    
    if mod < .1:
        mod = .1

Delete everything after mod = float(mv)/float(cskill.maxValue) and replace it with this

    mod = 1.0

In the same function find the following

    if proto.projectile:
        p = Projectile(mob,mob.target)
        p.spellProto = proto
        p.launch()                
    else:
        SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod,True)

Change the last line to this

        SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod,skillname)

/mud/world/spell.py

A lot of changes here due to the magic/skill structure change. Enjoy.

In class SpellProto?(Persistent): replace each entry with the one listed below. Should all be string fields after you are done. You should only be replacing, you aren't deleting or changing anything else.

    castTime =   StringCol(default="30")
    recastTime = StringCol(default="60")
    duration = StringCol(default = "")
    castRange = StringCol(default="20")
    aoeRange = StringCol(default="")
    manaCost = StringCol(default="") #for setting mana requirements

Find the class Spell and change the definit line listed below to the following:

class Spell(Process):
    def __init__(self,src,dst,spellProto,mod = 1.0,time = 0,skill = "",spitem = None,doParticles = True,fromStore = False,level = 1):

A few lines down find the following

        self.time = time
        self.mod = mod
        self.spellEffectInfo = None #pb.Cacheable
        self.effects = []
        self.skill = skill
        self.doParticles = doParticles

After it add this line

        self.spitem = spitem

Find the following bit of code

    # generator function
    def tick(self):
        proto = self.spellProto
        while 1:
            if proto.duration == 0:
                yield True  # will exit next tick
            if self.time >= proto.duration:
                return

Replace it with this bit

    # generator function
    def tick(self):
        proto = self.spellProto
        while 1:
            if not proto.duration:
                yield True  # will exit next tick
            if self.time >= ParseStatValue(proto.duration,self.src,self.skill,None,proto,self.spitem):
                return

Find the function for CheckResist? and replace the definition with this one

def CheckResist(src,dst,proto,skill = ""):

Find the function for SpawnSpell? and replace the definition with this one

def SpawnSpell(proto,src,dst,pos = (0,0,0), mod = 1.0,skill="",spitem=None,spellLevel = 1):

Some lines down you will find the following bit of code

    # modify cast range by spell skill level, max + 20%
    castRange = proto.castRange
    if proto.skillname:
        try:
            slevel = src.skillLevels[proto.skillname]
            diff = slevel - (proto.level - 2) * 10
            if diff > 0:
                if diff > 20:
                    diff = 20
                castRange += castRange * 0.2 * float(diff) / 20.0
        except KeyError:
            pass

You need to make a change here, but the one I have included removes this bit of castRange code so you can control it yourself. If you want leave it in, but make sure to get the castRange change

    # removed by Xerves
    castRange = ParseStatValue(proto.castRange, src, skill,None,proto,spitem)

Find the following

    if proto.aoeRange:
        aoeRange = proto.aoeRange

Replace it all with the following

    if proto.aoeRange:
        aoeRange = ParseStatValue(proto.aoeRange, src, skill,None,proto,spitem)
        if aoeRange:            

Everything after that in the if proto.aoeRange needs to be bumped by 4 spaces since we introduced a new if check in there. It is about 80 lines or so.

At the very end of those lines you need to increase by 4 spaces you will see this.

            src.processesPending.append(Spell(src,m,proto,mod,0,skill))

Replace it with the following

                src.processesPending.append(Spell(src,m,proto,mod,0,skill,spitem))

A handful of lines down you will see the following

                    # all mobs in the same party are at the same location, only test once per party
                    if not passed:
                        if c.mob.zone == src.zone and GetRangeMin(c.mob,src) <= castRange:
                            passed = True
                        else:
                            break
                    src.processesPending.append(Spell(src,c.mob,proto,mod,0,skill,doParticles,False,spellLevel))

Replace that last line with this one

                    src.processesPending.append(Spell(src,c.mob,proto,mod,0,skill,spitem,doParticles,False,spellLevel))

A few lines down there is another processingPending call...replace it with this

                src.processesPending.append(Spell(src,c.mob,proto,mod,0,skill,spitem,doParticles,False,spellLevel))

Just 3 lines or so below that another one...replace it

            src.processesPending.append(Spell(src,src,proto,mod,0,skill,spitem,True,False,spellLevel))

Another one to replace some lines down, find the following

        if dstPlayer and dstPlayer.encounterSetting > srcPlayer.encounterSetting:
            srcPlayer.applyEncounterSetting(dstPlayer.encounterSetting, True)
             
    src.processesPending.append(Spell(src,dst,proto,mod,0,skill,True,False,spellLevel))

Replace that last line with this

    src.processesPending.append(Spell(src,dst,proto,mod,0,skill,spitem,True,False,spellLevel))

A handful of lines down find the following

            haste = 2.0
        if haste < 0.25:
            haste = 0.25
        self.timer = spellProto.castTime*haste

Replace the last line with this

        self.timer = ParseStatValue(spellProto.castTime, mob, "",None,spellProto,None) * haste

A few lines down there is this chunk of mana code

        self.manaCost = proto.manaCost
        if self.level != 1:
            self.manaCost += (self.level / 10.0) * 0.35  # 35% more mana at level 10
         
        # reduced mana cost depending on spell skill level, max -10%
        if proto.skillname:
            try:
                slevel = mob.skillLevels[proto.skillname]
                diff = slevel - (proto.level - 2) * 10
                if diff > 0:
                    if diff > 20:
                        diff = 20
                    self.manaCost -= int(round(self.manaCost * 0.1 * float(diff) / 20.0))
            except KeyError:
                pass

This will get rid of the manacost flux (arg). I replaced it all with this 1 line

        self.manaCost = ParseStatValue(proto.manaCost, mob, "",None,proto,None)

Some lines down find the following

                    player.sendGameText(RPG_MSG_GAME_DENIED,r'%s\'s casting failed, missing %s as component%s.\n'%(mob.name,', '.join('<a:Item%s>%i %s</a>'%(GetTWikiName(ip.name),c,ip.name) for ip,c in components.iteritems() if c),selector[len(components)>1 or (components.values()[0])>1]))
                    return False
         
        if proto.recastTime:
            mob.recastTimers[proto] = proto.recastTime

Replace the last 2 lines with this

        if proto.recastTime and proto.recastTime != "0":
            mob.recastTimers[proto] = ParseStatValue(proto.recastTime, mob, "",None,proto,None)

Some more liens down find the following

                    if not player.checkItems(componentsConsumed.keys(),componentsConsumed.values(),True):
                        selector = {True: 's', False: ''}
                        player.sendGameText(RPG_MSG_GAME_DENIED,r'%s\'s casting failed, missing %s as component%s.\n'%(mob.name,', '.join('<a:Item%s>%i %s</a>'%(GetTWikiName(ip.name),c,ip.name) for ip,c in componentsConsumed.iteritems() if c),selector[len(componentsConsumed)>1 or (componentsConsumed.values()[0])>1]))
                        if proto.recastTime and proto in mob.recastTimers:

Replace the last line with this

                        if proto.recastTime and proto.recastTime != "0" and proto in mob.recastTimers:

A few lines down find the following

                mob.zone.simAvatar.mind.callRemote("casting",mob.simObject.id,False)
                if player:
                    player.sendGameText(RPG_MSG_GAME_DENIED,r'%s\'s casting failed, no pet.\n'%mob.name)
                if proto.recastTime and proto in mob.recastTimers:

Again replace the last line

                if proto.recastTime and proto.recastTime != "0" and proto in mob.recastTimers:

Again the same thing a few more lines down find the following

                    mob.zone.simAvatar.mind.callRemote("casting",mob.simObject.id,False)
                    if player:
                        player.sendGameText(RPG_MSG_GAME_DENIED,r'%s\'s casting failed, no target.\n'%mob.name)
                    if proto.recastTime and proto in mob.recastTimers:

Replace that last line

                    if proto.recastTime and proto.recastTime != "0" and proto in mob.recastTimers:

One more...

                if not proto.aoeRange and not AllowHarmful(mob,tgt):
                    if player:
                        player.sendGameText(RPG_MSG_GAME_DENIED,r'%s\'s casting failed, cannot cast a harmful spell on this target.\n'%mob.name)
                    if proto.recastTime and proto in mob.recastTimers:

Change the last line

                    if proto.recastTime and proto.recastTime != "0" and proto in mob.recastTimers:

Another section of code that puts in skill level to influence our values, I am removing it

                castRange = proto.castRange
                if proto.skillname:
                    try:
                        slevel = mob.skillLevels[proto.skillname]
                        diff = slevel - (proto.level - 2) * 10
                        if diff > 0:
                            if diff > 20:
                                diff = 20
                            castRange += castRange * 0.2 * float(diff) / 20.0
                    except KeyError:
                        pass

Replace with...

                # removed by Xerves
                castRange = ParseStatValue(proto.castRange, mob, "",None,proto,None)

More recast Checks

            if len(proto.particleNodes):
                mob.zone.simAvatar.mind.callRemote("triggerParticleNodes",mob.simObject.id,proto.particleNodes)
            
            if proto.recastTime:
                mob.recastTimers[proto] = proto.recastTime

Last 2 lines change to

            if proto.recastTime and proto.recastTime != "0" :
                mob.recastTimers[proto] = ParseStatValue(proto.recastTime, mob, "",None,proto,None)

A few lines down find the following

                if self.level != 1.0:
                    mod += (self.level / 10.0) * 0.5
                 
                SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod,False,self.level)

Change the last line to

                SpawnSpell(proto,mob,tgt,tgt.simObject.position,mod,"",None,self.level)

Final change in this File! Find the following a few lines down

        mob.zone.simAvatar.mind.callRemote("casting",mob.simObject.id,False,True)
        mob.casting = None
         
        if self.spellProto.recastTime and self.spellProto in mob.recastTimers:

Replace the last line with this

        if self.spellProto.recastTime and self.spellProto.recastTime != "0" and self.spellProto in mob.recastTimers:

mud/client/gui/enclopediaWnd.py

Find the following

BOLD_PARSER = re.compile(r'\*+(.*?)\*+')

Change it to

BOLD_PARSER = re.compile(r'\*\*+(.*?)\*\*+')

More on this change a bit later....

mud/worlddocs/classpages.py

Find the following

            CLASSSKILLS[skillname][3][s.minReuseTime].append(c.name)
            CLASSSKILLS[skillname][4][s.maxReuseTime].append(c.name)

Change it to this

            CLASSSKILLS[skillname][3][s.reuseTime].append(c.name)
            CLASSSKILLS[skillname][4][s.reuseTime].append(c.name)

Find this lovely long ling

            CLASSSKILLS[skillname] = [[c.name], defaultdict(list, {s.levelGained: [c.name]}), defaultdict(list, {s.levelCapped: [c.name]}), defaultdict(list, {s.minReuseTime: [c.name]}), defaultdict(list, {s.maxReuseTime: [c.name]}), defaultdict(list, {s.maxValue: [c.name]}), defaultdict(list, {trained: [c.name]}), defaultdict(list), defaultdict(list)]

Replace it with this one

            CLASSSKILLS[skillname] = [[c.name], defaultdict(list, {s.levelGained: [c.name]}), defaultdict(list, {s.levelCapped: [c.name]}), defaultdict(list, {s.reuseTime: [c.name]}), defaultdict(list, {s.reuseTime: [c.name]}), defaultdict(list, {s.maxValue: [c.name]}), defaultdict(list, {trained: [c.name]}), defaultdict(list), defaultdict(list)]

mud/worlddocs/spellpages.py

In GenStatText? find this lovely code

    casttime = int(float(spell.castTime)/float(durSecond))
    if casttime:
        casttime = "%i seconds"%casttime
    else:
        casttime = "Instant"
    
    recasttime = int(float(spell.recastTime)/float(durSecond))
    if recasttime:
        recasttime = "%i seconds"%recasttime
    else:
        recasttime = "Instant"
    
    duration = int(float(spell.duration)/float(durSecond))
    if duration:
        duration = "%i seconds"%duration
    else:
        duration = "Instant"
    
    stext += " *Cast Time:* %s *Recast Time:* %s *Cast Range:* %im<br>"%(casttime,recasttime,spell.castRange)
    
    if spell.target not in (RPG_TARGET_SELF,RPG_TARGET_PARTY) or spell.aoeRange:
        stext += " *Duration:* %s <br>"%(duration)
    
    if spell.aoeRange:
        stext += " *AoE Range:* %im<br>"%(spell.aoeRange)
    
    if spell.manaCost:
        stext += " *Mana:* %i<br>"%(spell.manaCost)

Replace it with this not so lovely code

    casttime = spell.castTime
    if casttime and casttime != "0":
        casttime = "%%GREEN%%%s%%ENDCOLOR%% 1/6 second"%casttime
    else:
        casttime = "Instant"
    
    recasttime = spell.recastTime
    if recasttime and recasttime != "0":
        recasttime = "%%GREEN%%%s%%ENDCOLOR%% 1/6 second"%recasttime
    else:
        recasttime = "Instant"
    
    duration = spell.duration
    if duration:
        duration = "%%GREEN%%%s%%ENDCOLOR%% 1/6 second"%duration
    else:
        duration = "Instant"
    
    stext += "**Cast Time:** %%GREEN%%%s%%ENDCOLOR%%<br>**Recast Time:** %%GREEN%%%s%%ENDCOLOR%%<br>**Cast Range:** %%GREEN%%%s%%ENDCOLOR%%<br>"%(casttime,recasttime,spell.castRange)
    
    if spell.target not in (RPG_TARGET_SELF,RPG_TARGET_PARTY) or spell.aoeRange:
        stext += "**Duration:** %%GREEN%%%s%%ENDCOLOR%%<br>"%(duration)
    
    if spell.aoeRange:
        stext += "**AoE Range:** %%GREEN%%%s%%ENDCOLOR%%<br>"%(spell.aoeRange)
    
    if spell.manaCost:
        stext += "**Mana:** %%GREEN%%%s%%ENDCOLOR%%<br>"%(spell.manaCost)

This will put in string values for these since there is no way to compute these values without actual data to parse the values. I tried to make it clean as possible to make it readable. You will also notice ** instead of *. More on that in a bit.

I am going to post a replacement for the whole effect Text generation (def GenEffectText?(spell):). This won't be the cleanest looking, but it should get those values properly outputted.

def GenEffectText(spell):
    etext = []
    
    stages = {
        RPG_EFFECT_STAGE_GLOBAL: "**Global Stats:** ",
        RPG_EFFECT_STAGE_BEGIN: "**Begin Stats:** ",
        RPG_EFFECT_STAGE_TICK: "**Tick Stats:** ",
        RPG_EFFECT_STAGE_END: "**End Stats:** "
    }
    
    for e in spell.effectProtos:
        etext.append("---++++ %s\n"%e.name)
        
        # Summoning (Pet)
        if e.summonPet:
            etext.append("<br>**Summon Pet:** [[Spawn%s][%s]]"%(GetTWikiName(e.summonPet.name),e.summonPet.name))
        
        # Summoning (Item)
        if e.summonItem:
            summonItem = e.summonItem
            SPELLSUMMONITEMS[summonItem].add(spell)
            etext.append("<br>**Summon Item:** [[Item%s][%s]]"%(GetTWikiName(summonItem.name),summonItem.name))
        
        # Damage Reflection
        if e.dmgReflectionMax:
            etext.append("<br> **Damage Reflection:** %i%% of %s with a maximum of %i"%(int(e.dmgReflectionPercent*100.0),RPG_RESIST_TEXT[RESISTFORDAMAGE[e.dmgReflectionType]],e.dmgReflectionMax))
        
        if e.negate and e.negateMaxLevel:
            etext.append("<br>**Negate:** %i of max level %i"%(e.negate,e.negateMaxLevel))
        
        # Resurrection
        if e.flags&RPG_EFFECT_RESURRECTION:
            etext.append("<br>**Resurrection:** %i%% XP"%(int(e.resurrectionXP*100.0)))
        
        # Banishing
        if e.flags&RPG_EFFECT_BANISH:
            etext.append("<br>**Banishes targets Pet**")
        
        # Spell casting interrupt
        if e.flags&RPG_EFFECT_INTERRUPT:
            etext.append("<br>**Interrupts targets Spell Casting**")
        
        # Leeching
        if e.leechEffect:
            leech = e.leechEffect
            etext.append("<br>**Leech:** %s "%leech.leechType.upper())
            if leech.leechBegin:
                etext.append("**Begin:** %i "%leech.leechBegin)
            if leech.leechTick and leech.leechTickRate:
                etext.append("**Tick:** %%GREEN%%%s%%ENDCOLOR%% every %%GREEN%%%s%%ENDCOLOR%% 1/6 second "%(leech.leechTick,leech.leechTickRate))
            if leech.leechBegin:
                etext.append("**Begin:** %i "%leech.leechBegin)
        
        # Drain
        if e.drainEffect:
            drain = e.drainEffect
            etext.append("<br>**Drain:** %s "%drain.drainType.upper())
            if drain.drainBegin:
                etext.append("**Begin:** %i "%drain.drainBegin)
            if drain.drainTick and drain.drainTickRate:
                etext.append("**Tick:** %%GREEN%%%s%%ENDCOLOR%% every %%GREEN%%%s%%ENDCOLOR%% 1/6 second "%(drain.drainTick,drain.drainTickRate))
            if drain.drainBegin:
                etext.append("**Begin:** %i "%drain.drainBegin)
        
        # Regen
        if e.regenEffect:
            regen = e.regenEffect
            etext.append("<br>**Regen:** %s "%regen.regenType.upper())
            if regen.regenBegin:
                etext.append("**Begin:** %i "%regen.regenBegin)
            if regen.regenTick and regen.regenTickRate:
                etext.append("**Tick:** %%GREEN%%%s%%ENDCOLOR%% every %%GREEN%%%s%%ENDCOLOR%% 1/6 second "%(regen.regenTick,regen.regenTickRate))
            if regen.regenBegin:
                etext.append("**Begin:** %i "%regen.regenBegin)
        
        # Damage
        damage = list(e.damage)
        if len(damage):
            etext.append("<br>**Damage:** ")
            etext.append(', '.join("%s (%s)"%(RPG_RESIST_TEXT[RESISTFORDAMAGE[ed.type]],ed.amount) for ed in e.damage))
        
        stats = {
            RPG_EFFECT_STAGE_GLOBAL: defaultdict(int),
            RPG_EFFECT_STAGE_BEGIN: defaultdict(int),
            RPG_EFFECT_STAGE_TICK: defaultdict(int),
            RPG_EFFECT_STAGE_END: defaultdict(int)
        }
        for es in e.stats:
            stats[es.stage][es.statname] = es.value
        
        for stage,stext in stages.iteritems():
            if len(stats[stage]):
                etext.append("<br>%s<br>"%stext)
                if stage == RPG_EFFECT_STAGE_TICK:
                    etext.append("Tick Rate: %i<br>"%e.tickRate)
                for name,value in stats[stage].iteritems():
                    if name == "haste":
                        etext.append("%%GREEN%%HASTE %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "castHaste":
                        etext.append("%%GREEN%%CASTING HASTE %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "innateHaste":
                        etext.append("%%GREEN%%INNATE HASTE %%ENDCOLOR%%(%s)<br>"%value)

                    elif name == "regenHealth":
                        etext.append("%%GREEN%%HEALTH REGEN %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "regenMana":
                        etext.append("%%BLUE%%MANA REGEN %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "regenStamina":
                        etext.append("%%YELLOW%%STAMINA REGEN %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "move":
                        etext.append("%%GREEN%%MOVE %%ENDCOLOR%%(%s)<br>"%value)
                    elif name == "slow":
                        etext.append("%%RED%%SLOW %%ENDCOLOR%%(%s)<br>"%value)
                    else:
                        etext.append("%%GREEN%%%s %%ENDCOLOR%%(%s)<br>"%(name.upper(),value))
                
    
    return ''.join(etext)

mud/worlddocs/skillpages.py

Find the following

    if len(classSkills[4]) == 1:
        skillPage += " *Max Reuse Time:* %i<br>"%classSkills[4].keys()[0]
    else:
        skillPage += " *Max Reuse Time:* %s<br>"%', '.join('%i seconds (%s)'%(int(float(maxReuseTime)/float(durSecond)),', '.join('[[Class%s][%s]]'%(GetTWikiName(cname),cname) for cname in classnames)) for maxReuseTime,classnames in classSkills[4].iteritems())
    
    if len(classSkills[3]) == 1:
        skillPage += " *Min Reuse Time:* %i<br>"%classSkills[3].keys()[0]
    else:
        skillPage += " *Min Reuse Time:* %s<br>"%', '.join('%i seconds (%s)'%(int(float(minReuseTime)/float(durSecond)),', '.join('[[Class%s][%s]]'%(GetTWikiName(cname),cname) for cname in classnames)) for minReuseTime,classnames in classSkills[3].iteritems())

Replace these lines with these

    if len(classSkills[3]) == 1:
        skillPage += "**Reuse Time:** %%GREEN%%%s%%ENDCOLOR%% 1/10 second<br>"%classSkills[3].keys()[0]
    else:
        skillPage += "**Reuse Time:** %s<br>"%', '.join('%%GREEN%%%s%%ENDCOLOR%% 1/6 second (%s)'%(reuseTime,', '.join('[[Class%s][%s]]'%(GetTWikiName(cname),cname) for cname in classnames)) for reuseTime,classnames in classSkills[3].iteritems())

Rest of Enclopedia changes

You might of noticed that * has been replaced with ** in the previous changes. This is to allow the * character to display in a formula. ** is now the character used for bold. You need to open up every file in /mud/world/worlddocs and change every * to ** (around 120 or so changes). You will see some \t* in the files too. You do not need to update these, it isn't being parsed. Once this is done you need to compile your enclopedia. You can do a publishpatch or you can do it manually by using the /mud/worlddocs/gendocs.py gameconfig=mygame.cfg command. Make sure it compiles with no errors.

Finally done...kind of

You now need to go through every skill and replace minReuseTime and maxReuseTime with reuseTime before you compile. Once this is done you should be able to compile your code and prey. Once that compiles with no errors start the game. If it doesn't start check your Client log for an indicator where it failed.