Let me know if you find anything wrong with this. On OSI
not all skills lower at the same rate. This is a somewhat unknown fact so there is not any solid information on how each skill lowers. I'd imagine that the skill would lower at a rate about equal to the rate at which it gains. That is, magery/blacksmithy would lower more slowly than camping.
Code:
[ - What is this? - ]
A packet hook for send skill requests so players can send and recieve skill lock information. It stores the information on a property called "SkillLocks" on the player. This package was coded for the 096 distro so if you're not using the 096 distro and you want to use it, you'll have to make some modifications. Or you could just help the 096 distro come out faster (or wait for it to come out, if you're lazy).
This should handle all skill request modes (full list, single skill update, full list with skill cap and single skill with cap) but my client must be retarded or something because I could never get it to request full list with skill cap or single skill with cap. However, I removed the checks for mode and sent the information anyway and everything _seems_ like it would work. If you know when a client requests full list with skill cap or single skill with cap, let me know please.
[ - Installation - ]
This requires a change of the distro's /pkg/systems/attributes/include/advanceCheck.inc. Add one line at the top:
include "attributes_ex";
And replace CheckSkillAdvance() with this function here:
function CheckSkillAdvance(mobile, skill_name, award_diff, pass_chance)
var skillLocks := GetObjProperty(mobile, "SkillLocks");
var lowerSkill := 0; // Name of skill set to lower
var skillId := AP_AttributeNameToSkillId(skill_name);
if (AP_CheckSkillCap(mobile, skill_name))
SkillDbgMsg(mobile, "Skill cap for attribute '"+skill_name+"' already reached.");
return 0;
elseif (AP_CheckSkillCap(mobile))
// Check if skill is not set to lock
if (skillLocks[skillId + 1] != 2)
// Find another skill that we can lower
var i;
for (i := 1; i <= skillLocks.size(); i := i + 1)
if (CInt(skillLocks[i]) == 1)
// Skill is set to lower, get its name
lowerSkill := AP_SkillIdToAttributeName(i - 1);
// If the skill set to be lowered is at 0, don't count it
if (AP_GetTrueSkill(mobile, lowerSkill) == 0)
lowerSkill := 0;
else
// lowerSkill is valid, so no more need to loop
i := skillLocks.size() + 1;
endif
endif
endfor
endif
// Do not return 0 if there is another skill we can lower
if (lowerSkill)
SkillDbgMsg(mobile, "Skill cap reached, but lowering " + lowerSkill + " to compensate.");
else
SkillDbgMsg(mobile, "Total skill cap already reached.");
return 0;
endif
else
// Check if skill is set to lower or is locked
if (skillLocks[skillId + 1] == 1 || skillLocks[skillId + 1] == 2)
SkillDbgMsg(mobile, "Skill " + skill_name + " set to lower or locked.");
return 0;
endif
endif
var skill_cap := AP_GetSkillCap(skill_name);
var true_skill := AP_GetTrueSkill(mobile, skill_name);
var limit_left := CDbl(skill_cap - true_skill);
var adv_check := RandomInt(100)+1;
var settings := AP_GetSettingsCfgElem("Skills");
var multiplier := CDbl(settings.GainSpeed);
if (true_skill < CDbl(settings.FreeGainUntil))
SkillDbgMsg(mobile, "Skill is < "+CDbl(settings.FreeGainUntil)+". Allowing for advancement");
elseif ( DifficultyTooEasy(true_skill, award_diff))
SkillDbgMsg(mobile, "No challenge. No advance. True skill ("+true_skill+"-20) > "+award_diff);
return 0;
elseif (adv_check <= pass_chance)
SkillDbgMsg(mobile, "Passed advance check: +"+(pass_chance - adv_check)+"%");
else
SkillDbgMsg(mobile, "Did not pass skill advance check.");
return 0;
endif
SkillDbgMsg(mobile, "Skill Cap: "+skill_cap+"%");
SkillDbgMsg(mobile, "Left to cap: "+limit_left+" ->"+skill_cap+"-"+true_skill);
SkillDbgMsg(mobile, "Multiplier: "+multiplier);
var int_chance := AP_GetStat(mobile, "Intelligence") / 2.0;
var task_chance := (100.0 - award_diff) / 2.0;
var chance := CInt((int_chance + task_chance) * multiplier);
SkillDbgMsg(mobile, "INT gives "+int_chance+"%");
SkillDbgMsg(mobile, "Task gives "+task_chance+"%");
SkillDbgMsg(mobile, "Multiplier on "+(int_chance+task_chance)+" gives "+chance);
SkillDbgMsg(mobile, "---");
var check_roll := RandomInt(100);
SkillDbgMsg(mobile, "Check %age: "+check_roll);
if (check_roll < chance)
var advance := 0.1;
if (award_diff > true_skill)
advance := CDbl(0.1 * (1.0 + CInt((award_diff - true_skill) / 5.0)));
endif
if (advance > limit_left)
advance := limit_left;
elseif (advance < 0.1)
advance := 0.1;
endif
SkillDbgMsg(mobile, "Passed. Advance "+skill_name+": " + advance);
var temp;
// If there's a skill to be lowered, do that now
if (lowerSkill)
// If the skill to be lowered is too low to allow for advancement, reduce advancement
// Example: gaining 0.2 when the skill to be lowered is 0.1 will reduce the gain to 0.1
// I'm not sure if this is how OSI does it, but you don't often gain more than 0.1
if (advance > AP_GetTrueSkill(mobile, lowerSkill))
advance := AP_GetTrueSkill(mobile, lowerSkill);
endif
// Make sure it goes down at least 0.1
temp := AP_GetTrueSkill(mobile, lowerSkill) - advance;
while (CInt(temp * 10.0) == CInt(GetAttributeBaseValue(mobile, lowerSkill)))
temp := temp - 0.1;
sleepms(2);
endwhile
AP_SetTrueSkill(mobile, lowerSkill, temp);
endif
temp := true_skill+advance;
while (CInt(temp*10.0) == CInt(GetAttributeBaseValue(mobile, skill_name)))
// Kludge for an old core precision bug.
// Makes sure it always goes up atleast 0.1.
temp := temp+0.1;
sleepms(10);
endwhile
AP_SetTrueSkill(mobile, skill_name, temp);
return 1;
endif
SkillDbgMsg(mobile, "Failed advancement check. No advance.");
return 0;
endfunction
NOTE: If you're NOT using the 096 distro then it's going to be a little more difficult. I don't think the 095 distro currently equivalent functions to the 096 attributes package. You're going to have to change doSkillLock.src to not use functions from that package, which will probably be difficult. The simplest thing you could do is remove the modes concerning skill caps. Editing attributes.inc is going to also be a little tricky, but the general logic you should follow is this:
if at skill cap
if skill not set locked
foreach other_skill for player
if other_skill set to lower
allow skill to advance
lower other_skill by same amount
endif
endforeach
endif
else
// not at skill cap
if skill set to locked or set to lower
do not allow gain
endif
endif
NOTE: On OSI, some skills decrease easier than others. However, this is a often overlooked detail so finding information on it is difficult. An elegant way to implement this may require more editing than I'd like to require in one of my packages. If I ever get around to adding it in, I'll at least share how to do it.
I have tested this on the 096 distro and everything seems to work. But, you never know. I also did my taxes from 2 years ago while coding some of this last night so I might have forgotten something stupid :-P.
[ - Considerations - ]
Thanks to Yukiko for testing this and reporting a compile problem!
[ - Contact - ]
If you have any questions or bugs, please let me know. Also, if you get this package working with the 095 distro, please share how on the forums.
E-mail: tekproxy@gmail.com
AIM: tekproxy
[edit]
Fixed a bug in my modifications for advanceCheck.inc
Added some stuff to the readme
[/edit]