1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-03 15:17:04 -05:00
OpenDiablo2/d2core/d2records/skill_details_loader.go
presiyan-ivanov 88326b5278
Initial cast overlay implementation. Fix HeroSkill deserialization & map entities processing crashing for remote client. (#766)
* Casting a skill now plays the corresponding overlay(if any).

* Prevent a crash caused by nil pointer in HeroSkill deserialization, happening when
unmarshalling HeroSkill from packets as a remote client.

* Add PlayerAnimationModeNone to handle some of the Skills(e.g.
Paladin auras) having "" as animation mode.

* Joining a game as remote client now waits for map generation to finish
before rendering map or processing map entities. This is temporary hack to prevent the game from
crashing due to concurrent map read & write exception.

* Send CastSkill packet to other clients.

Co-authored-by: Presiyan Ivanov <presiyan-ivanov@users.noreply.github.com>
2020-10-10 18:47:51 -04:00

323 lines
13 KiB
Go

package d2records
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2calculation/d2parser"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt"
)
// LoadSkills loads skills.txt file contents into a skill record map
//nolint:funlen // Makes no sense to split
func skillDetailsLoader(r *RecordManager, d *d2txt.DataDictionary) error {
records := make(map[int]*SkillRecord)
parser := d2parser.New()
for d.Next() {
name := d.String("skill")
parser.SetCurrentReference("skill", name)
record := &SkillRecord{
Skill: d.String("skill"),
ID: d.Number("Id"),
Charclass: d.String("charclass"),
Skilldesc: d.String("skilldesc"),
Srvstfunc: d.Number("srvstfunc"),
Srvdofunc: d.Number("srvdofunc"),
Prgstack: d.Bool("prgstack"),
Srvprgfunc1: d.Number("srvprgfunc1"),
Srvprgfunc2: d.Number("srvprgfunc2"),
Srvprgfunc3: d.Number("srvprgfunc3"),
Prgcalc1: parser.Parse(d.String("prgcalc1")),
Prgcalc2: parser.Parse(d.String("prgcalc2")),
Prgcalc3: parser.Parse(d.String("prgcalc3")),
Prgdam: d.Number("prgdam"),
Srvmissile: d.String("srvmissile"),
Decquant: d.Bool("decquant"),
Lob: d.Bool("lob"),
Srvmissilea: d.String("srvmissilea"),
Srvmissileb: d.String("srvmissileb"),
Srvmissilec: d.String("srvmissilec"),
Srvoverlay: d.String("srvoverlay"),
Aurafilter: d.Number("aurafilter"),
Aurastate: d.String("aurastate"),
Auratargetstate: d.String("auratargetstate"),
Auralencalc: parser.Parse(d.String("auralencalc")),
Aurarangecalc: parser.Parse(d.String("aurarangecalc")),
Aurastat1: d.String("aurastat1"),
Aurastatcalc1: parser.Parse(d.String("aurastatcalc1")),
Aurastat2: d.String("aurastat2"),
Aurastatcalc2: parser.Parse(d.String("aurastatcalc2")),
Aurastat3: d.String("aurastat3"),
Aurastatcalc3: parser.Parse(d.String("aurastatcalc3")),
Aurastat4: d.String("aurastat4"),
Aurastatcalc4: parser.Parse(d.String("aurastatcalc4")),
Aurastat5: d.String("aurastat5"),
Aurastatcalc5: parser.Parse(d.String("aurastatcalc5")),
Aurastat6: d.String("aurastat6"),
Aurastatcalc6: parser.Parse(d.String("aurastatcalc6")),
Auraevent1: d.String("auraevent1"),
Auraeventfunc1: d.Number("auraeventfunc1"),
Auraevent2: d.String("auraevent2"),
Auraeventfunc2: d.Number("auraeventfunc2"),
Auraevent3: d.String("auraevent3"),
Auraeventfunc3: d.Number("auraeventfunc3"),
Auratgtevent: d.String("auratgtevent"),
Auratgteventfunc: d.String("auratgteventfunc"),
Passivestate: d.String("passivestate"),
Passiveitype: d.String("passiveitype"),
Passivestat1: d.String("passivestat1"),
Passivecalc1: parser.Parse(d.String("passivecalc1")),
Passivestat2: d.String("passivestat2"),
Passivecalc2: parser.Parse(d.String("passivecalc2")),
Passivestat3: d.String("passivestat3"),
Passivecalc3: parser.Parse(d.String("passivecalc3")),
Passivestat4: d.String("passivestat4"),
Passivecalc4: parser.Parse(d.String("passivecalc4")),
Passivestat5: d.String("passivestat5"),
Passivecalc5: parser.Parse(d.String("passivecalc5")),
Passiveevent: d.String("passiveevent"),
Passiveeventfunc: d.String("passiveeventfunc"),
Summon: d.String("summon"),
Pettype: d.String("pettype"),
Petmax: parser.Parse(d.String("petmax")),
Summode: d.String("summode"),
Sumskill1: d.String("sumskill1"),
Sumsk1calc: parser.Parse(d.String("sumsk1calc")),
Sumskill2: d.String("sumskill2"),
Sumsk2calc: parser.Parse(d.String("sumsk2calc")),
Sumskill3: d.String("sumskill3"),
Sumsk3calc: parser.Parse(d.String("sumsk3calc")),
Sumskill4: d.String("sumskill4"),
Sumsk4calc: parser.Parse(d.String("sumsk4calc")),
Sumskill5: d.String("sumskill5"),
Sumsk5calc: parser.Parse(d.String("sumsk5calc")),
Sumumod: d.Number("sumumod"),
Sumoverlay: d.String("sumoverlay"),
Stsuccessonly: d.Bool("stsuccessonly"),
Stsound: d.String("stsound"),
Stsoundclass: d.String("stsoundclass"),
Stsounddelay: d.Bool("stsounddelay"),
Weaponsnd: d.Bool("weaponsnd"),
Dosound: d.String("dosound"),
DosoundA: d.String("dosound a"),
DosoundB: d.String("dosound b"),
Tgtoverlay: d.String("tgtoverlay"),
Tgtsound: d.String("tgtsound"),
Prgoverlay: d.String("prgoverlay"),
Prgsound: d.String("prgsound"),
Castoverlay: d.String("castoverlay"),
Cltoverlaya: d.String("cltoverlaya"),
Cltoverlayb: d.String("cltoverlayb"),
Cltstfunc: d.Number("cltstfunc"),
Cltdofunc: d.Number("cltdofunc"),
Cltprgfunc1: d.Number("cltprgfunc1"),
Cltprgfunc2: d.Number("cltprgfunc2"),
Cltprgfunc3: d.Number("cltprgfunc3"),
Cltmissile: d.String("cltmissile"),
Cltmissilea: d.String("cltmissilea"),
Cltmissileb: d.String("cltmissileb"),
Cltmissilec: d.String("cltmissilec"),
Cltmissiled: d.String("cltmissiled"),
Cltcalc1: parser.Parse(d.String("cltcalc1")),
Cltcalc2: parser.Parse(d.String("cltcalc2")),
Cltcalc3: parser.Parse(d.String("cltcalc3")),
Warp: d.Bool("warp"),
Immediate: d.Bool("immediate"),
Enhanceable: d.Bool("enhanceable"),
Attackrank: d.Number("attackrank"),
Noammo: d.Bool("noammo"),
Range: d.String("range"),
Weapsel: d.Number("weapsel"),
Itypea1: d.String("itypea1"),
Itypea2: d.String("itypea2"),
Itypea3: d.String("itypea3"),
Etypea1: d.String("etypea1"),
Etypea2: d.String("etypea2"),
Itypeb1: d.String("itypeb1"),
Itypeb2: d.String("itypeb2"),
Itypeb3: d.String("itypeb3"),
Etypeb1: d.String("etypeb1"),
Etypeb2: d.String("etypeb2"),
Anim: animToEnum(d.String("anim")),
Seqtrans: d.String("seqtrans"),
Monanim: d.String("monanim"),
Seqnum: d.Number("seqnum"),
Seqinput: d.Number("seqinput"),
Durability: d.Bool("durability"),
UseAttackRate: d.Bool("UseAttackRate"),
LineOfSight: d.Number("LineOfSight"),
TargetableOnly: d.Bool("TargetableOnly"),
SearchEnemyXY: d.Bool("SearchEnemyXY"),
SearchEnemyNear: d.Bool("SearchEnemyNear"),
SearchOpenXY: d.Bool("SearchOpenXY"),
SelectProc: d.Number("SelectProc"),
TargetCorpse: d.Bool("TargetCorpse"),
TargetPet: d.Bool("TargetPet"),
TargetAlly: d.Bool("TargetAlly"),
TargetItem: d.Bool("TargetItem"),
AttackNoMana: d.Bool("AttackNoMana"),
TgtPlaceCheck: d.Bool("TgtPlaceCheck"),
ItemEffect: d.Number("ItemEffect"),
ItemCltEffect: d.Number("ItemCltEffect"),
ItemTgtDo: d.Number("ItemTgtDo"),
ItemTarget: d.Number("ItemTarget"),
ItemCheckStart: d.Bool("ItemCheckStart"),
ItemCltCheckStart: d.Bool("ItemCltCheckStart"),
ItemCastSound: d.String("ItemCastSound"),
ItemCastOverlay: d.String("ItemCastOverlay"),
Skpoints: parser.Parse(d.String("skpoints")),
Reqlevel: d.Number("reqlevel"),
Maxlvl: d.Number("maxlvl"),
Reqstr: d.Number("reqstr"),
Reqdex: d.Number("reqdex"),
Reqint: d.Number("reqint"),
Reqvit: d.Number("reqvit"),
Reqskill1: d.String("reqskill1"),
Reqskill2: d.String("reqskill2"),
Reqskill3: d.String("reqskill3"),
Restrict: d.Number("restrict"),
State1: d.String("State1"),
State2: d.String("State2"),
State3: d.String("State3"),
Delay: d.Number("delay"),
Leftskill: d.Bool("leftskill"),
Repeat: d.Bool("repeat"),
Checkfunc: d.Number("checkfunc"),
Nocostinstate: d.Bool("nocostinstate"),
Usemanaondo: d.Bool("usemanaondo"),
Startmana: d.Number("startmana"),
Minmana: d.Number("minmana"),
Manashift: d.Number("manashift"),
Mana: d.Number("mana"),
Lvlmana: d.Number("lvlmana"),
Interrupt: d.Bool("interrupt"),
InTown: d.Bool("InTown"),
Aura: d.Bool("aura"),
Periodic: d.Bool("periodic"),
Perdelay: parser.Parse(d.String("perdelay")),
Finishing: d.Bool("finishing"),
Passive: d.Bool("passive"),
Progressive: d.Bool("progressive"),
General: d.Bool("general"),
Scroll: d.Bool("scroll"),
Calc1: parser.Parse(d.String("calc1")),
Calc2: parser.Parse(d.String("calc2")),
Calc3: parser.Parse(d.String("calc3")),
Calc4: parser.Parse(d.String("calc4")),
Param1: d.Number("Param1"),
Param2: d.Number("Param2"),
Param3: d.Number("Param3"),
Param4: d.Number("Param4"),
Param5: d.Number("Param5"),
Param6: d.Number("Param6"),
Param7: d.Number("Param7"),
Param8: d.Number("Param8"),
InGame: d.Bool("InGame"),
ToHit: d.Number("ToHit"),
LevToHit: d.Number("LevToHit"),
ToHitCalc: parser.Parse(d.String("ToHitCalc")),
ResultFlags: d.Number("ResultFlags"),
HitFlags: d.Number("HitFlags"),
HitClass: d.Number("HitClass"),
Kick: d.Bool("Kick"),
HitShift: d.Number("HitShift"),
SrcDam: d.Number("SrcDam"),
MinDam: d.Number("MinDam"),
MinLevDam1: d.Number("MinLevDam1"),
MinLevDam2: d.Number("MinLevDam2"),
MinLevDam3: d.Number("MinLevDam3"),
MinLevDam4: d.Number("MinLevDam4"),
MinLevDam5: d.Number("MinLevDam5"),
MaxDam: d.Number("MaxDam"),
MaxLevDam1: d.Number("MaxLevDam1"),
MaxLevDam2: d.Number("MaxLevDam2"),
MaxLevDam3: d.Number("MaxLevDam3"),
MaxLevDam4: d.Number("MaxLevDam4"),
MaxLevDam5: d.Number("MaxLevDam5"),
DmgSymPerCalc: parser.Parse(d.String("DmgSymPerCalc")),
EType: d.String("EType"),
EMin: d.Number("EMin"),
EMinLev1: d.Number("EMinLev1"),
EMinLev2: d.Number("EMinLev2"),
EMinLev3: d.Number("EMinLev3"),
EMinLev4: d.Number("EMinLev4"),
EMinLev5: d.Number("EMinLev5"),
EMax: d.Number("EMax"),
EMaxLev1: d.Number("EMaxLev1"),
EMaxLev2: d.Number("EMaxLev2"),
EMaxLev3: d.Number("EMaxLev3"),
EMaxLev4: d.Number("EMaxLev4"),
EMaxLev5: d.Number("EMaxLev5"),
EDmgSymPerCalc: parser.Parse(d.String("EDmgSymPerCalc")),
ELen: d.Number("ELen"),
ELevLen1: d.Number("ELevLen1"),
ELevLen2: d.Number("ELevLen2"),
ELevLen3: d.Number("ELevLen3"),
ELenSymPerCalc: parser.Parse(d.String("ELenSymPerCalc")),
Aitype: d.Number("aitype"),
Aibonus: d.Number("aibonus"),
CostMult: d.Number("cost mult"),
CostAdd: d.Number("cost add"),
}
records[record.ID] = record
}
if d.Err != nil {
return d.Err
}
r.Skill.Details = records
log.Printf("Loaded %d Skill records", len(records))
return nil
}
func animToEnum(anim string) d2enum.PlayerAnimationMode {
switch anim {
case "SC":
return d2enum.PlayerAnimationModeCast
case "TH":
return d2enum.PlayerAnimationModeThrow
case "KK":
return d2enum.PlayerAnimationModeKick
case "SQ":
return d2enum.PlayerAnimationModeSequence
case "S1":
return d2enum.PlayerAnimationModeSkill1
case "S2":
return d2enum.PlayerAnimationModeSkill1
case "S3":
return d2enum.PlayerAnimationModeSkill3
case "S4":
return d2enum.PlayerAnimationModeSkill4
case "A1":
return d2enum.PlayerAnimationModeAttack1
case "A2":
return d2enum.PlayerAnimationModeAttack2
case "":
return d2enum.PlayerAnimationModeNone
default:
log.Fatalf("Unknown skill anim value [%s]", anim)
}
// should not be reached
return d2enum.PlayerAnimationModeNone
}