Heute möchte ich euch ein Tutorial der ganz besonderen Art präsentieren. Um es zu verstehen benötigt ihr gewisse Grundkenntnisse des Codings, deswegen empfehle ich euch einige der Grund-Tutorials im Coding-Bereich zu downloaden und durchzuarbeiten.
Es handelt sich bei diesem Tutorial mehr um einen Auszug, d.h. ich werde nicht jeden einzelnen Schritt erklären und keine weiteren Verweise angeben. Nur die wirklich entscheidenden Dinge werden von mir erklärt und angegeben. Auch hierbei gilt immer wieder: Learning bei Doing, ohne Fleiß kein Preis.

Das heutige Thema lautet: Random Weapons
Was ist also mit diesem Ausdruck gemeint? Schon häufig wurden Fragen gestellt: Wie kann man einer beliebigen Einheit oder einem Helden verschiedene Weapons geben, die aber zufällig funktionieren? Wie kann ich ein zufälliges Object spawnen?
All diese Fragen werden in diesem Tutorial beantwortet.

Man entscheidet in diesem Fall zwischen normalen Random-Angriffen (die über die jeweilige Grundweapon eines Objects laufen) und einer Fähigkeits-Random-Weapon (eine Fähigkeit wird aktiviert, hat aber zufällige Auswirkungen)
Um euch das System an einem Beispiel näher zu bringen:
Mit diesem System können wir einem Bogenschützen beispielsweise eine Zufälligkeit zwischen normalen Pfeilen, Silberdornpfeilen und sonstigen anderen Pfeilen geben.
Oder wir können einen Kadaverregen in einen Elementarregen umändern, bei dem je nach Zufall entweder Leichen, Eis oder Feuerbrocken vom Himmel fallen.

Folgende INIs werden benötigt:
Weapon.ini
Helden/Einheit.ini
objectcreationlist.ini

Vorweg ein Hinweis: ca.90% aller Fähigkeiten können in einen OCL umgewandelt werden, wodurch bei jeder Fähigkeit theoretisch eine Randomweapon eingebaut werden könnte. Diesen Schritt erkläre ich jedoch in einem anderen Tutorial.

1. Variante: Grundweapon
2. Variante: Fähigkeitsweapon

1) Als erstes gehen wir in die Helden/Einheiten.ini und suche nach den Angriffsanimationen, diese findet ihr unter dem Suchbegriff: FIRING_OR_PREATTACK_A
Ich erkläre das Beispiel anhand des Gebirgsriesen, durch den ich auf dieses äußerst simple System gestoßen bin.
Ihr schaut euch nun die Animation an, die ungefähr wie folgend aufgebaut sein wird:

        AnimationState = FIRING_OR_PREATTACK_A
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATKA
                AnimationMode        = ONCE
                UseWeaponTiming      = Yes
                AnimationBlendTime   = 10
            End
        End

Dies beschreibt die PRIMARY Weapon. Scrollt ein wenig nach unten und sucht diesen Eintrag: WeaponSet

Dort findet ihr jetzt die aufgelisteten Weapons. Normalerweise ist nur eine angegeben und zwar die PRIMARY, als die Grundweapon. Hinzu gibt es ein Script, das besagt, dass immer genau diese Weapon eingesetzt wird. Beispiel:
    AutoChooseSources = PRIMARY FROM_PLAYER FROM_SCRIPT FROM_AI

Wir möchten nun aber eine Weapon haben, die gerade nicht immer die Grundweapon nutzt, deswegen brauchen wir ein besonderes System vom Gebirgsriesen.
Geht in seine INI und sucht seinen Weaponeintrag.
Ihr werdet dies finden:
    WeaponSet
        Conditions = None
        DefaultWeaponChoiceCritera = SELECT_AT_RANDOM
        ReadyStatusSharedWithinSet = Yes
        
        Weapon            = PRIMARY        WildMountainGiantPunch1        // ATKA - Left arm swat
        Weapon            = SECONDARY        WildMountainGiantPunch2        // ATKB - Right backhand
        Weapon            = TERTIARY        WildMountainGiantPunch3        // ATKD - Right football kick

        Weapon            = QUATERNARY    GiantBuildingTeardown        // ATKE - Left forward kick
        OnlyAgainst        = QUATERNARY    MONSTER STRUCTURE BLOCKING_GATE
        
        Weapon            = QUINARY        WildMountainGiantMovingPunch    // ATRA - Right backhand while moving
        OnlyInCondition    = QUINARY        MOVING
    End

Einigen wird jetzt vielleicht schon ein Licht aufgehen, falls nicht: Nur Geduld.

Weapons können an bestimmte Voraussetzungen geheftet werden, wie z.B. "in der Bewegung". Dabei muss einfach nur der folgende Code genutzt werden:

OnlyInCondition    = QUINARY        MOVING

Das ist aber nicht Teil meines Tutorials, deswegen kann ich euch nur empfehlen dies mal ein wenig genauer zu untersuchen. Wir beschäftigen uns hiermit:
        DefaultWeaponChoiceCritera = SELECT_AT_RANDOM
        ReadyStatusSharedWithinSet = Yes

Diese Codes geben an, dass wir es hier mit mehreren Weapons zutun haben. Wir können jetzt entweder Weapons coden, die nur auf bestimmte KindOfs wirken, wie z.B. PRIMARY wirkt nur auf INFANTRY, oder wir erstellen eine Randomweapon.

Wichtig bei einer Randomweapon ist, dass wir keine Einschränkungen angeben und diese auf keine bestimmten TAGs beziehen, wie oben angegeben. 
Sucht nun wieder die Angriffsanimation auf und scrollt ein wenig runter. Ihr findet dies:

        // ATKA - Left arm swat
        AnimationState = FIRING_OR_PREATTACK_A
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATKA
                AnimationMode        = ONCE
                UseWeaponTiming        = Yes
                AnimationBlendTime    = 10
            End
        End
        
        // ATKB - Right backhand
        AnimationState = FIRING_OR_PREATTACK_B
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATKB
                AnimationMode        = ONCE
                UseWeaponTiming        = Yes
                AnimationBlendTime    = 10
            End
        End

        // ATKD - Right football kick    
        AnimationState = FIRING_OR_PREATTACK_C
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATKD
                AnimationMode        = ONCE
                UseWeaponTiming        = Yes
                AnimationBlendTime    = 10
            End
        End

        // ATKE - Left forward kick        
        AnimationState = FIRING_OR_PREATTACK_D                            ; The anti-structure weapon
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATKE
                AnimationMode        = ONCE
                UseWeaponTiming        = Yes
                AnimationBlendTime    = 10
            End
        End

        // ATRA - Right backhand while moving
        AnimationState = FIRING_OR_PREATTACK_E
            Animation
                AnimationName        = WUMntGnt_SKL.WUMntGnt_ATRA
                AnimationMode        = ONCE
                UseWeaponTiming        = Yes
                AnimationBlendTime    = 10
            End
            FXEvent    = Frame:3 FireWhenSkipped Name:FX_GiantRightFootStep
            FXEvent    = Frame:20 FireWhenSkipped Name:FX_GiantLeftFootStep
        End

Der AnimationState ist an den TAG der Weapon geknüpft. Hier eine Auflistung zur Erklärung:
FIRING_OR_PREATTACK_A = PRIMARY
FIRING_OR_PREATTACK_B = SECONDARY
FIRING_OR_PREATTACK_C = TERTIARY
FIRING_OR_PREATTACK_D = QUATERNARY
FIRING_OR_PREATTACK_E = QUINARY

Wenn wir jetzt unsere Weapons auf keinen TAG beziehen, die nötigen RandomSets angegeben haben und die nötigen Animationen gecodet haben, so können wir je nach Animation eine andere Weapon auslösen. Folglich benötigen wir für jede Weapon eine andere Animation, was Voraussetzung für dieses System ist.
Wir könnten jetzt Elrond nehmen, ihm drei Weapons geben wie oben und an jede Animation einen anderen AnimationState heften.
Das ist schon die große Kunst und ist eigentlich nachvollziehbar.

2) Fähigkeitsweapons.
Hierbei brauchen wir nur ein zusätzliches Module:

Object AngmarGulzarCorpseRainVariation
    BuildVariations = AngmarGulzarCorpseRainOne AngmarGulzarCorpseRainTwo AngmarGulzarCorpseRainThree
End

Dieses Module kann in jedem Zwischenschritt eingebaut werden und wirkt sich in keinster Weise auf Abläufe, Animationen oder sonstiges aus. Es ist einfach nur ein Zwischenparameter, der im OCL eingefügt werden kann und angibt: Mit einer Wahrscheinlichkeit von 1/3 wird Variation a, b, oder c ausgelöst.
Dieses System könnte man beispielsweise an die Grundweapons von Lurtz Waffe heften. Dazu schauen wir uns seine Weapon an:

Weapon IsenguardLurtzBow       ; BALANCE Archer Weapon ;orc archer
    AttackRange            = LURTZ_BOW_RANGE                    ; 350 ; Horde member so it needs to be bigger than the horde weapon attack range.
    
    RangeBonusMinHeight      = 30 ;    = 10
    RangeBonus        = 1
    RangeBonusPerFoot    = #MULTIPLY( LURTZ_BOW_RANGE .003 )  
    
    LeechRangeWeapon    = Yes    
    WeaponSpeed            = 300         ; dist/sec 
    MinWeaponSpeed        = 121
    MaxWeaponSpeed        = 400      ; dist/sec Upper limit on scaling, when attacking past nominal "max" range
    FireFX                = FX_RohanArcherBowWeapon  
    ScaleWeaponSpeed    = Yes ; Used for lob weapons, scales speed proportional to range
    HitPercentage        = 100     ;When this weapon is used it will hit exactly 50% of the time.
    ScatterRadius        = 40.0     ;When this weapon misses it can randomly miss by as much as this distance.

    AcceptableAimDelta    = 20    ; prevent twitchy reaiming in horde on horde
    DelayBetweenShots    = LURTZ_BOW_DELAYBETWEENSHOTS    ; time between shots, msec
    PreAttackDelay      = LURTZ_BOW_PREATTACKDELAY
    PreAttackType       = PER_POSITION ; Do the delay each time we attack a new target
    FiringDuration      = LURTZ_BOW_FIRINGDURATION        ; Duration of the archer firing shot is 500ms.
;    RequireFollowThru    = Yes
;    IdleAfterFiringDelay = 0 ; don't set for any weapon other than rocks. 
;    HoldAfterFiringDelay = 1333 ; don't set for any weapon other than rocks. 

    ClipSize            = 1
    AutoReloadsClip        = Yes
    AutoReloadWhenIdle    = 1    
    ClipReloadTime        = Min:LURTZ_BOW_RELOADTIME_MIN Max:LURTZ_BOW_RELOADTIME_MAX
    ContinuousFireOne    = 0
    ContinuousFireCoast    = LURTZ_BOW_RELOADTIME_MAX

    AntiAirborneVehicle    = Yes
    AntiAirborneMonster    = Yes

    ProjectileNugget            ; Fire arrow available through fire upgrade
        ProjectileTemplateName  = EvilFactionArrow
        WarheadTemplateName     = IsengardLurtzBowWarhead
    End    
End

Das Projectile kann jetzt verändert werden in dieses: EvilFactionArrowVariation

Nun suchen wir das eigentliche Projectile und finden dies hier:

Object EvilFactionArrow
  ; *** ART Parameters ***
  Draw = W3DScriptedModelDraw ModuleTag_Draw
    DefaultModelConditionState     
      Model = NONE
    End
  End

  Draw = W3DStreakDraw ModuleTag_Draw2
	Length		= 15
	Width		= 2
	NumSegments	= 1				; number of segements in the streak, increase for smoother line
	Color		= R:255 G:255 B:255
	Additive	= No					; Yes by default
	Texture		= EXArrowStreak01.tga
	WeatherTexture = SNOWY EXArrowStreak_Snow.tga
  End

  ; *** AUDIO Parameters ***

  ; ***DESIGN parameters ***
  EditorSorting     = SYSTEM

  ArmorSet
    Armor = NoArmor
  End

  VisionRange = 0.0

  ; *** ENGINEERING Parameters ***
  KindOf = PROJECTILE NO_COLLIDE HIDE_IF_FOGGED
  Body = ActiveBody ModuleTag_02
    MaxHealth       = 100.0
  End

  Behavior = DestroyDie ModuleTag_03
    ;nothing
  End
    
  Behavior = BezierProjectileBehavior ModuleTag_04

    FirstHeight = 9  ; Height of Bezier control points above highest intervening terrain
    SecondHeight = 9
    FirstPercentIndent = 20% ; Percentage of shot distance control points are placed
    SecondPercentIndent = 90%
    FlightPathAdjustDistPerSecond = 50 ; Can allow a max speed this can attempt to follow a target. Units are their velocity we can tag. (45 is default human speed)
    GroundHitFX = FX_GondorArrowDeath
    CurveFlattenMinDist = 100.0
  End

;  Behavior = CreateObjectDie ModuleTag_06
 ;   DeathTypes = ALL
 ;   CreationList = OCL_EvilFactionShatteredArrowBits
 ; End

 ; Behavior = FXListDie ModuleTag_07
 ;   DeathTypes = ALL -CRUSHED -SPLATTED
 ;   DeathFX = FX_GondorArrowDeath
 ; End

  Geometry = Sphere
  GeometryIsSmall = Yes
  GeometryMajorRadius = 0.8
End

Wir bringen einfach unseren Zwischenschritt ein:

Object EvilFactionArrowVariation
    BuildVariations = EvilFactionArrow BarbedArrowProjectile PoisonArrowProjectile EvilFactionFireArrow
End

Durch dieses System würde nun entweder ein normaler Pfeil, ein Stachelpfeil, ein Gift-Pfeil oder ein Feuerpfeil ausgelöst werden. Da es sich hierbei um keine Zeitablauf-Einschränkungen handelt, gäbe es also keine Probleme in dem System ;) Das Projectile stirbt hierbei nicht, hat keine KindOfs oder irgendwelche sonstigen Dinge. Es ist einfach nur ein randomsystem, bei dem per Zufall ausgesucht wird, welches Projectile genutzt wird.

Das Ganze kann auch als Zwischenschritt in einem OCL, bei einem ARROW_STORM ENUM eingebaut werden, wie ich es bei Gulzar getan habe.
Probiert einfach ein wenig aus und ihr werdet feststellen, wie genial dieses System ist.

Falls ihr Probleme oder dergleichen habt, könnt ihr diese gerne im Forum melden www.modding-union.com
Ein Readmeeintrag in eurer Mod wäre sehr entgegenkommend, wenn ihr dieses System nutzt.

Ealendril der Dunkle