Master Level Overhaul

Discussions on various DOL development features

Moderator: Support Team

Re: Master Level Overhaul

Postby Leodagan » Thu Sep 25, 2014 9:31 am

For curiosity :

Here is my hybrid spell implementation in about 50 lines ;) (supporting multiple baseline or specline attached from specs)

It's not really tested, but it's to give some example on how a whole "career" can be handled using some fine tuned query ;) (Performance may be low with this kind of code, but it get cached pretty good by the runtime !)

If I were to drop the "auto"-merge capability of DOL Core Hybrid spell line handling it could get half of the code :)
Code: Select all
public override IDictionary<SpellLine, List<Spell>> GetLinesSpellsForLiving(GameLiving living) { Dictionary<SpellLine, IEnumerable<Tuple<Spell, int, bool>>> buffer = new Dictionary<SpellLine, IEnumerable<Tuple<Spell, int, bool>>>(); IList<SpellLine> lines = GetSpellLinesForLiving(living); foreach (SpellLine ls in lines) { var lib = SkillBase.GetSpellList(ls.KeyName).Where(item => item.Level <= ls.Level); int take = 1; if (living is GamePlayer && AllowMultipleSpellVersions(ls, (GamePlayer)living)) { // Get 2-First Better Spell for each type take = 2; } var firstBetterSpellNoGroup = lib.Where(item => item.Group == 0) .GroupBy(item => new { item.SpellType, item.Target, item.IsAoE, item.IsInstantCast, item.HasSubSpell }) .Select(ins => ins.OrderByDescending(it => it.Level).ThenByDescending(it => it.ID).Take(take)); var firstBetterSpellGroup = lib.Where(item => item.Group != 0) .GroupBy(item => item.Group) .Select(ins => ins.OrderByDescending(it => it.Level).ThenByDescending(it => it.ID).Take(take); // no need for sorting we'll use the order collection for this... var firstBetterSpellFinal = firstBetterSpellGroup.SelectMany(el => el).Union(firstBetterSpellNoGroup.SelectMany(el => el)).Where(item => item != null); // Get Appearance Order // not group base var baseOrderNoGroup = lib.Where(item => item.Group == 0) .GroupBy(item => new { item.SpellType, item.Target, item.IsAoE, item.IsInstantCast, item.HasSubSpell }) .Select(ins => ins.OrderBy(it => it.Level).ThenBy(it => it.ID).FirstOrDefault()); // group based var baseOrderGroup = lib.Where(item => item.Group != 0) .GroupBy(item => item.Group) .Select(ins => ins.OrderBy(it => it.Level).ThenBy(it => it.ID).FirstOrDefault()); // Join and sort... var baseOrderFinal = baseOrderNoGroup.Union(baseOrderGroup).Where(item => item != null).OrderBy(item => item.Level).ThenBy(item => item.ID).ToList(); // Join all this in the Dictionary ! We have to save appearance index order here (as it will be merged later) // the previous "baseOrderFinal" query is used as an index range... var finalList = firstBetterSpellFinal.Select(item => new Tuple<Spell, int, bool>(item, baseOrderFinal.FindIndex(od => (od.Group == 0 && (od.SpellType == item.SpellType && od.Target == item.Target && od.IsAoE == item.IsAoE && od.HasSubSpell == item.HasSubSpell && od.IsInstantCast == item.IsInstantCast)) || (od.Group != 0 && od.Group == item.Group)) , ls.IsBaseLine)); if (!buffer.ContainsKey(ls)) { buffer.Add(ls, finalList); } } // bind them in ONE spellLine return buffer.GroupBy(entry => entry.Key.Spec) .ToDictionary(k => lines.First(), v => v.SelectMany(el => el.Value) .OrderBy(el => (el.Item3 ? 0 : 1)) .ThenBy(el => el.Item2) .ThenBy(el => el.Item1.Level) .Select(Level => Level.Item1).ToList()); }
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Tolakram » Thu Sep 25, 2014 12:03 pm

Shouldn't it be GetSpellLinesForLiving?

:D
- Mark
User avatar
Tolakram
Storm / Storm-D2 Admin
 
Posts: 9189
Joined: Tue Jun 13, 2006 1:49 am
Location: Kentucky, USA

Re: Master Level Overhaul

Postby Leodagan » Thu Sep 25, 2014 12:17 pm

GetSpellLinesForLiving(living) already exists, it's called inside this method ;)

Here we want the line's Spells ! it's given as a dict to retrieve both baseline and specline togeter... for hybrid this dict should have only one element... For List-Caster if someone tries to add more than one baseline/specline to a character it could list all of them in there :)

To update : Dict Aren't ordered so this is not enough to keep line's order, thus I rely on the spec itself GetSpellLines to get the order in which it's sent to client :) I'm updating some gameplayer method to force the order to the spec.GetSpellLines before giving it to packetlib !

Edit : Funny thing I should have mention the "List cast" handler, 13 lines ! :twisted:
Code: Select all
public virtual IDictionary<SpellLine, List<Spell>> GetLinesSpellsForLiving(GameLiving living) { IDictionary<SpellLine, List<Spell>> dict = new Dictionary<SpellLine, List<Spell>>(); foreach (SpellLine sl in GetSpellLinesForLiving(living)) { dict.Add(sl, SkillBase.GetSpellList(sl.KeyName) .Where(item => item.Level <= sl.Level) .OrderBy(item => item.Level) .ThenBy(item => item.ID).ToList()); } return dict; }
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Argo » Thu Sep 25, 2014 8:12 pm

Oh Argo you were reading ;)

Sorry I got this bad habit of trying to use technical word depending of who I'm speaking to :D

For World Builder here is a shorter summary :

new Table : Class X Specialization : Class Id -> (Spec Key, RequiredLevel), ...
new Field : Specialization.Implementation (Varchar) that can use any Classname of an object Type subclassing "Specialization" (like NPC.ClassType that allow to customize Mobs class type !!)

Specialization now links, Table SpellLine (using Field : Spec), Spec X Ability (using Field : Spec), Style (using Field : Spec KeyName)

Which mean that most skills for a gameplayer will autoload from database relations to their Specialization (Writing Custom Code to extend Specialization will allow any coder to handle these relations in the way they want...)

The new "ALL-Table Relations" around this "Class X Specialization Table" is what I called "Character Career Descriptor" as it should be enough to compute all the skills a player will get in its playthrough, The Custom "Specialization" Object that can be used through "Specialization.Implementation" Field is what I called "Career Manager Class".

If Tomorrow I want to change the "Fire Magic" of a wizard for a "Lightning Magic", it's one record in class x spec to change and it's done :), If I want to make a specialization that send baseline spells as "hybrid list" and specline spells as "list caster" I can extend the class "LiveSpellLineSpecialization" dedicated to list caster, overriding the method "public virtual IDictionary<SpellLine, List<Spell>> GetLinesSpellsForLiving(GameLiving living)" to change the shape of the Spell list sent by network (... displayed in skill list and trainers...)

To make a new class it stills need some code but it will be way easier : Add a classID to GlobalConstants, Create a subclass of CharacterBaseClass with the Attribute referencing this new classID (copying another advanced class will be fine...) and just write all database records to describe the spec/skills/style/abilities/spells he will gain through leveling or training and you're done !
Well THAT's a language i do understand :D and yes, thats a great idea. And for another markup, YES OFCOURSE I DO READ :mrgreen:

taske care :)

Argo
Möge Gott sein zwischen Dir und dem Leid, an allen dunklen und verlassenen Orten, die Du erreichen wirst.
Argo
Server Team
 
Posts: 1760
Joined: Thu Sep 18, 2008 6:21 pm
Location: Berlin, Germany

Re: Master Level Overhaul

Postby Leodagan » Thu Sep 25, 2014 9:14 pm

Ok this is taking way longer than I though but I like the result :

For now all Specs and class abilities are implemented in data-record, I'll have to work around champion / RA / ML after that ;)

I'm doing the first tests with updated SendUpdatePlayerSkills() and SendTrainerWindow() packet for 1.109 only, the "Career" compute can take up to 50 ms on a Core i5 (this is a lot !) but once it's done most update are done in 0ms ... (thanks to Linq cache somewhere ??)

For a live-like "behavior" this is doing what's expected, I'll have to update other packet Lib versions, and make some more tests, and I'll have to build Custom Specialization to handle Specs like RA / CL that offer the ability to "Choose" from skills, not following a list !

Use Skill Handler and PacketLib will get way easier to understand like this, the only thing I'm afraid is about the order I send Abilities, I'm pretty sure this can mess up skill bar somehow, but I need some more test again :D
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Leodagan » Fri Sep 26, 2014 11:29 am

I'm fighting a bit with the client ;)

It looks like it's expecting ability in a given order...

I'm relying on caches and forced order on most collection to be sure the client and server understand them well when speaking about skills like : "I want to use the skill at this position in the skill list you sent me !"

But I still have some display bug depending on specific hardcoded client handling...

I'm wondering what is really the client expecting for Ability displaying, this is really minding me :)

DOL only send ability with all level to 0, I think this handles them as "Realm Ability" in fact, when trying to set real level in abilities, everything goes wrong :)

-----

Update :

I'll look into packet 1.112 for Ability that handle levels ;) it may not be expected in 1.109...

Currently everything work with basic spelline/styleline and abilities, in skill update sending, list caster sending, trainer window, delving in trainer preview, delving in ui bar, delving in skill list, displaying ability summary, displaying spec summary, etc...

Nothing have been test around RA, ML or CL, it's hard to see that in one week I just got to the starting point :)

There are some database "tuning" to display the specs in expected order (base class specs first) I can use negative "required level" for this, (as long as it's under level 2 it's given at character start...), I keep the "1" value for RA, the "40" value for ML and the "50" value for CL everything between this and all INT.MinValue can be used to sort custom spec otherwise ;)

Currently there is some kind of "auto-update" on character loading, all non-custom spellline goes away (they're detected as being in career) all non-custom abilities goes away (same as spellline) and they're is chance that once I set the RA they'll be prune of Serialized abilities and put into Serialized Realm Ability (to keep order and level !)

No changes here as I intend to keep some scripted behavior for everything that isn't found in career (but it need more testing !!)

Later any "ML" or "CL" value would be retrieved from script too but it will need something that converts them...
For ML's I can easily use an int to code everything (2 byte = ML line, 14 bytes = 14 spells by line with boolean value)

Champion Spell got their "serialized" line, I should use it to store their respective level in each "lines"

Still a lot to do but it's getting interesting now that everything is clean :D
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Leodagan » Mon Sep 29, 2014 6:59 pm

I finished some Testing around 1.109 and 1.115+ version (1.110+ is for tooltip tests)

I added some methods to force tooltip update each time we send a new "skill" to the gameclient, this will prevent cache missmatch between shards !

I updated the "order" of player specs to be displayed mostly live like, I use the RequiredLevel in negative value to set order, any negative value will be treated the same as being "level 1" but it will be used for "OrderBy" before sending specs to client...

I fixed some trouble with tooltip using some "server side" cache to know what the client asked and when, preventing from flooding the server with tooltip request ! (for now the timeout is set to 1 hour it's pretty much conservative !!)

I fixed how style displays their followup and requirement style to display the name in tooltip.

I entered the "hardcoded" tooltip for Abilities into storm local copy to match the live abilities.

Everything is working flawless, I need to test some more class to be sure I did not miss anything...

After these test I think I'll make a first commit, this seems like a good step, only class are handled by DataObject, all Champions, RA, ML can work the same as before without further modifications ;)
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Tolakram » Mon Sep 29, 2014 9:34 pm

It doesn't change the way spells acre calculated for hybrids, does it? THe order they are sent is critical to keep the toolbad from jumbling on levelup. I don't think it would, just making sure.
- Mark
User avatar
Tolakram
Storm / Storm-D2 Admin
 
Posts: 9189
Joined: Tue Jun 13, 2006 1:49 am
Location: Kentucky, USA

Re: Master Level Overhaul

Postby Leodagan » Tue Sep 30, 2014 5:33 am

That's the part I was testing yesterday evening ;)

The "LevelRequired" on a Specialization is simply a constraint checked when refreshing a player specs list (on level up and on train)

In gameplayer code there is something like :
Code: Select all
foreach(Specialization spec in SkillBase.GetSpecsByClass(CharacterClass.ID).Where(spec => spec.RequiredLevel <= Level)) AddSpecialization(spec);
As player always start level 1 this is why every value under or equal "1" for RequiredLevel will have the same effect ;)


For "Skills" Level it obeys either Style.RequiredLevel, SpecXAbility.AbilityLevel, or LineXSpell.Level...

You're right for Hybrids spell "juggling" my test showed that the order I force for sending Hybrid is not the right one...

I must Admit I re-writed the hybrid code based on what I thought was the right order after reading quickly the GetUsableSpells() Methods ;)

There is some comment from you in there, maybe you could remember if there was a simple rule of thumb for the hybrid order ?

I have some packet log from live server and could not really determine an order based on spell level or spell appearance, and I don't know how the client handle the "skill list index" when we're adding abilities to a character (as abilities are always send first before styles and spells !)

I'll have some more research to do around this for now...
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Tolakram » Tue Sep 30, 2014 7:25 am

The key to hybrid order was using spellgroup to identify like spells and then sending spell types (groups) in the exact same order every time.

So if at level 2 the player gets an AF buff as the 3rd spell then the AF buff should be sent every time as the 3rd spell. Eventually at level 7 they have been given a total of 15 spells and they get a better AF buff as the 15th spell but it should be sent as the 3rd spell in their list, replacing the previous AF spell. As long as the AF buff spell is sent 3rd their toolbar won't scramble but will simply replace the older spell with the improved version.

A hybrid might get a total of 50 spells by level 50, but we only send unique spells based on spellgroup, and we send them in the same order every time. The annoying exception is pure healers, who get both the last 2 versions of a spell instead of just the last version.

List casters don't have this issue since they always get new spells.
- Mark
User avatar
Tolakram
Storm / Storm-D2 Admin
 
Posts: 9189
Joined: Tue Jun 13, 2006 1:49 am
Location: Kentucky, USA

Re: Master Level Overhaul

Postby Leodagan » Tue Sep 30, 2014 7:37 am

I don't have my packet Logs at work, but I'm pretty sure that the code I made should "order by appearance" when sending hybrids...

This is where I used a "GroupBy" Method to select the "Lowest" spell of each type, then sort them by Level and use this result as "Hybrid Type Appearance Order"...

I know by your explanations that my code won't order correctly for multiple "spell versions" users, as it will list the same "Type" together, and it looks like it should need another "Hybrid Type Second Level Appearance Order", to append to the first list.

But the part annoying me is that I tested Around Minstrel (yes I'm masochist with Songs skills in between of Spells :D) and I'm pretty sure they only have "one version" of each spell (I'll tell you more when I had some time reading my logs, I was short on time and could just launch some tests up to now).

Edit :

And an other question that minds me, how to display specs and base ? If I have a Cleric level 10 that spec enhancement, and later get a new "baseline" spell, how do we sort the spells ? The spell must be "ordered" when "Updating" only ? and once the player relog it can sort it out if we send the line in different order ?
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Tolakram » Tue Sep 30, 2014 8:48 am

I did this using the spellgroup column in the spell table. The old version tried to use type and it never worked, spellgroup is key. (not to be confused with effectgroup which is used for something else).

You have to think of it as a ordered list of things a player gets as they level up.

So let's say at level 4 they have 4 spells, one in each group. Number below is the group.

1. AF1 (1)
2. STR1 (2)
3. CON1 (3)
4. HEAL1 (4)

At level 10 they haven't trained yet but are still receiving baseline spells.

1. AF2 (1)
2. STR2 (2)
3. CON2 (3)
4. HEAL2 (4)
5. SMITE2 (5)

Now they decide to train in smite and enhancement.

1. AF2 (1)
2. STR2 (2)
3. CON2 (3)
4. HEAL2 (4)
5. SMITE2 (5)
11. DEX/STR1 (6)
15. SPEC_SMITE1 (7)

It doesn't matter when they train or how much they train, all that matters is the spell list given to them is in the same order, by spellgroup, every time. Think of it this way. When they place spells on their toolbar they are not placing the spell names but instead the spellgroup number. I don't recall if we send any ordering number to the client or not.

If the player decides to train rejuv at level 11.

1. AF2 (1)
2. STR2 (2)
3. CON2 (3)
4. HEAL2 (4)
5. SMITE2 (5)
9. SPEC_HEAL1 (6)
11. DEX/STR1 (7)
15. SPEC_SMITE1 (8)

This does not jumble the bar. :o I do not remember why, all I know is it works.

As long as the same method always handles sending the spell list, and it does it the same way every time for the same player then everything is fine. If a player respecs the order may change for that player and their bar may jumble. This happens on live as well, last I checked.
- Mark
User avatar
Tolakram
Storm / Storm-D2 Admin
 
Posts: 9189
Joined: Tue Jun 13, 2006 1:49 am
Location: Kentucky, USA

Re: Master Level Overhaul

Postby Tolakram » Tue Sep 30, 2014 8:59 am

^^ edited the above post at least 5 times. :)
- Mark
User avatar
Tolakram
Storm / Storm-D2 Admin
 
Posts: 9189
Joined: Tue Jun 13, 2006 1:49 am
Location: Kentucky, USA

Re: Master Level Overhaul

Postby Leodagan » Tue Sep 30, 2014 9:19 am

Yeah I think mostly nothing can be done for respeccing except trying to rebuild the list in the same order it was previously, but indexes won't match if the player don't train again in similar skills...

I clearly understand the logic around "Skill Index" the client use, it only needs the packetlog display to see clearly the "Skill Table" sent at login and analyze which skill is at which position...

What you miss in your example is the "LineIndex" that is being sent by server (which match the Spec Index, so even hybrid doesn't have only ONE spell line...)

So you're getting me a bit lost here :)

Because the "heal" Spec spell will append the "heal" spec line, that's why it's not ordered !!

If I rewrite all your example :

1. AF1 (1) (line buff)
2. STR1 (2) (line buff)
3. CON1 (3) (line buff)
4. HEAL1 (4) (line heal)

At level 10 they haven't trained yet but are still receiving baseline spells.

1. AF2 (1) (line buff)
2. STR2 (2) (line buff)
3. CON2 (3) (line buff)
4. HEAL2 (4) (line heal)
5. SMITE2 (5) (line smite)

Now they decide to train in smite and enhancement.

1. AF2 (1) (line buff)
2. STR2 (2) (line buff)
3. CON2 (3) (line buff)
4. HEAL2 (4) (line heal)
5. SMITE2 (5) (line smite)
11. DEX/STR1 (6) (line buff)
15. SPEC_SMITE1 (7) (line smite)

If the player decides to train rejuv at level 11.

1. AF2 (1) (line buff)
2. STR2 (2) (line buff)
3. CON2 (3) (line buff)
4. HEAL2 (4) (line heal)
5. SMITE2 (5) (line smite)
9. SPEC_HEAL1 (6) (line heal)
11. DEX/STR1 (7) (line buff)
15. SPEC_SMITE1 (8) (line smite)


Maybe you'll need a 6th edit ;)
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon

Re: Master Level Overhaul

Postby Leodagan » Tue Sep 30, 2014 9:36 am

I'm making new post to prevent cross-edits :

Here is the Example that Annoys me the most :

Cleric Level 1


1. AF1 (index 0, line 0 = buff)
1. HEAL1 (index 1, line 1 = heal) // is it recorded by client as index 0 of line 1 ??
1. SMITE1 (index 2, line 2 = smite)

Cleric Level 2 : speccing in heal to level faster, getting new baselevel spell

1. AF1 (index 0, line 0)
2. STR1 (index 1, line 0)
1. HEAL1 (index 2, line 1)
2. CURE1 (index 3, line 1)
2. SPEC_HEAL1 (index 4, line 1)
1. SMITE1 (index 5, line 2)
2. STUN1 (index 6, line 2)

Here if the Client don't keep track of "SkillIndex" depending on "LineIndex" everything is screwed ? my "Base" heal get from position 1 to position 2 ?

Or Should Level 2 be : ???

1. AF1 (index 0, line 0)
1. HEAL1 (index 1, line 1)
1. SMITE1 (index 2, line 2)
2. STR1 (index 3, line 0)
2. CURE1 (index 4, line 1)
2. STUN1 (index 5, line 2)
2. SPEC_HEAL1 (index 6, line 1)

In either Case :

At Level 3 my Cleric gain "Baseline Rez" where does it goes ? after Spec_Heal1 ? after Cure1 ? (nothing sent to client tells him appart between base and spec spell so it will screw skillbar...)

Any way appending is not hard, but I can't keep this order between different session ? What happen If I don't send the same order to the client each time he logs on ?
User avatar
Leodagan
Developer
 
Posts: 1350
Joined: Tue May 01, 2012 9:30 am
Website: https://daoc.freyad.net
Location: Lyon


Return to “%s” DOL Development Discussion

Who is online

Users browsing this forum: No registered users and 1 guest