- Introduction
- WARNING
- Values for skills/spells affected by this change
- mud/world/core.py
- mud/world/shared/playdata.py
- mud/world/effect.py
- mud/world/crafting.py
- mud/world/item.py
- mud/world/mobspells.py
- mud/world/combat.py
- mud/world/mob.py
- mud/world/skill.py
- /mud/world/spell.py
- mud/client/gui/enclopediaWnd.py
- mud/worlddocs/classpages.py
- mud/worlddocs/spellpages.py
- mud/worlddocs/skillpages.py
- Rest of Enclopedia changes
- Finally done...kind of
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.

