Strength, Lifting, Armwrestling, Dexterity, Agility, Movement, Jumping, Dancing, Singing, CastFireBallSpell, Cooking.
Which are attributes and which are skills? Hard to tell, eh? And even harder to motivate.
So I will merge the concepts. They are all skills.
There are two central classes: Skill and SkillSet. Skill is a simple root interface that looks something like this:
interface Skill {
int getValue();
}
It's just a value holder in other words. This already gives us an advantage - the actual value for some skills can be derived from other skills. So the value of a movement skills might be 50% based on strength and agility of the same person.
SkillSet is a keyed collection of skills (like a Hashtable):
class SkillSet {
void addSkill(Object key, Skill skill);
void removeSkill(Skill skill);
Enumeration getSkills();
Enumeration getSkills(Class skillType);
}
Each Creature will have a SkillSet. I'll talk more about SkillSets later in this document.
Each weapon works differently so the AttackResultEvent will ask the shortSword to produce an attackSuccessIndex of some sort, given the attacker's SkillSet. In this example shortSword determines that:
attackSuccessIndex = shortSwordSkill + (swordSkill * 3) + combatSkill
For a huge TwoHandedBattleSword it might look like this:
attackSuccessIndex = 2HBattleSwordSkill + (swordSkill * 2) + combatSkill + (Strength * 2)
Note these formulas are just examples! But they illustrate how to include skill dependency. Here we only include "upwards dependency", however, where your success at ShortSword depends on both the ShortSwordSkill and the SwordSkill. But what about "downwards" dependency, where an improvement in DaggerSkill should give an indirect improvement in ShortSwordSkill?
Downwards dependency can be modelled directly in the Skill. So when your DaggerSkill is improved, it will automatically improve your SwordSkill as well by some fraction. And the SwordSkill will then automatically improve your CombatSkill by some fraction. So an expert at swordfighting will also be reasonably good at combat in general, and thereby we able to use almost any weapon with reasonable confidence. If a weapon is so weird and complex that you can't use it at all if you don't have any skill in it, it's attackSuccessIndex formula might look like this:
attackSuccessIndex = WeirdWeaponSkill * 5
Or something like that. In this case the result is 0 if you don't have this specific skill at all. But Julio will solve the specific mathematical relationships...
public static final String SKILL_STRENGTH = "strength";
We can also "encapsulate" parts of the skill system by adding methods like this to Creature:
public int getStrength() {
Skill str = skills.getSkill(SKILL_STRENGTH);
if (str == null)
return 0;
else
return str.getValue();
}
This means we can hide the "super generical" nature of the skill system and provide a simple interface for some common attributes/skills.
To find all sword combat skills I can write: getSkills(orb.skills.SwordCombatSkill), or to find all combat related spells I can write: getSkills(orb.skills.CombatSkill), etc. We can use all kinds of interfaces and class hierarchies in the orb.skills.* package to organize our different skills types, TeachableSkill for all skills that can be improved by teaching.
Note that we do not need one Skill class for each single weapon type, each single attribute, and so on. We only need new classes for skills that work differently internally, or skills that we want to organize in categories using the class hierarchy. So instead of ShortSwordCombatSkill, DaggerCombatSkill, etc we can use just SwordCombatSkill an include an instance variable for which Sword class the skill applies to.
We may also use global definition of skills, sort of like they have done with system properties. Basically just a text file of skill names that we agree to use, such as "skill.combat", "skill.fishing", "skill.combat.sword". "skill.magic.detectMagic", etc. Now if I want to find a skill I just write: skillSet.getSkill("skill.fishing").
After we have coded a few skills and skill-related activities we can decide on exactly which technique to use when finding skills.
I propose putting all skills in the body, even if it may sound boring. Why? Well because the Soul is "merely" an Effect. If the SingingSkill is stored in the Soul's skill set, then it won't be used if someone casts a curse on me that forces me to sing. Another reason is that, even though Soul may initiate an action, it is the body that is registered as the "source" of the action. That means the Action itself does not deal with the soul at all - it just knows that Fred (the body) wants to stab his sword at the orc. So it's a lot cleaner if the action doesn't have to also worry about who actually made Fred attack the Orc, and can pick up the Skills directly from Fred's skill set.
Well when I join the priest order, an ArghahathFilter is added to my collection. If I ever get killed, this filter will be activitated and check my ArghahathMeditationSkill. If it is high, I may be resurrected - i.e. healed up and teleported to the high temple of Argha. Cool eh? And all this without having to modify any core classes. Just add new ones for that priest order.
If I manage to convert someone else, he may also be given the Arghahath filter and skill. But he must keep up his meditation skill, it deteriorates quickly with time...
Action related skills (perhaps tagged with the ActionSkill Interface?) can be improved when the action related to the skill is executed. So all skills that implement the ActionSkill interface by adding this method:
void skillUsed(GameEvent event);
CombatSkill is an action related skill and it's implementation works like this: if the attack succeeded there is a % chance that the CombatSkill improves by a few points. The higher the skills value is, the less the chance of improvement. If the attack fails there is also a chance, but smaller. If the attack critically fails (or something like that) the chance is very high that you improve.
We might also have an interface called TeachableSkill, with the following method:
void lessonReceived(TeachSkillEvent lesson);
The lesson includes information about who the teacher is (or perhaps just his SkillSet), his skill at teaching, the duration of the lesson, and stuff like that. The implementation of receiveLesson will vary for different skills depending on how easy there are to teach, but in most cases they will improve by a certain amount, modified by factors such as the intelligence of the student, the difference between the skill level of the student and the teacher, etc.
And then, of course, we probably want a PracticeableSkill interface that adds this method:
void skillPracticed(PracticeSkill event);
You get the idea.
So the SpyEffect decides which language to use, creates a Skill object (or maintains one as an instance variable), and creates a TalkEvent where the source is the Sword. So this is sort of a case where the Skill is stored in the soul. Which means if someone else casts a SpySpell with less language skills, the two SpySpells will not interfere with each other's skill levels.
This is fun. Let's say we have a spy sword with this effect that only speaks a really strange unknown language. You really badly want to find out what the sword has seen, but you can't understand it's language so you have to find a translator :o)
Last updated: