The Forums Are Now Closed!

The content will remain as a historical reference, thank you.

[Tutorial] How to mod/create items and *New how to add them to the shop

Worked example of creating an item from an ability can be found in post 2.

By on February 17, 2010 4:43:50 AM from Demigod Forums Demigod Forums

Exxcentric

Join Date 07/2009
+47

Ok so i havent done much of a search to go through what tutorials have been posted but i thought that i would start this thread so ppl can understand the item related code more easily. I will try to make this comprehensive but it may be a work in progress for a while. Also feel free to contribute!

Ok,

There are 3 levels of an item

ItemBlueprint {

                   Abilities = {

                   AbilityBlueprint{

                                      Buffs{

                                      BuffBlueprint{ }

                                      }

                  }

                 }

}

Item Blueprint is the data that is refered to when you click purchase at the shop. It contains information about tooltips, Display name, item description and item icon. Note that alot of these are repeated in the higher levels.

Ability Blueprint is the code that initiates an action. Ability blueprints contain information about the ability type (the action which causes the ability like an aura or targeted or on use) icon displayed when an ability is active.

BuffBlueprint performs an action for a given duration. Ie modifies  health or mana speed etc.

____________________________________________

Ok so a simple item to start off with ItemBluePrint (using scaled helm) I will break down this as much as i can as a basic overview:


Name = 'Item_Helm_010',  

Referal Name, each item must have a different name

DisplayName = '<LOC ITEM_Helm_0000>Scaled Helm',  

Name on Tooltip. Notice  - LOC ITEM_Helm_0000 - must be diffent for each new tooltip, ie you could change it to LOC ITEM_Helm_0100


    GetManaBonus = function(self) return Buffs['Item_Helm_010'].Affects.MaxEnergy.Add end, 

Self update function for tooltip display of max mana  ([GetManaBonus]).


    GetManaRegenBonus = function(self) return Buffs['Item_Helm_010'].Affects.EnergyRegen.Add end,

see above, but for mana per second. Notice how both of these are '.Add' .

It can be:     GetManaRegenBonus = function(self) return math.floor(Buffs['Item_Helm_030'].Affects.EnergyRegen.Mult * 100) end, for buff affects which use the mult = function.


    Tooltip = {
        Bonuses = {
            '<LOC ITEM_Helm_0001>+[GetManaBonus] Mana', -- What is shown of tooltip using [GetManaBonus] function
            '<LOC ITEM_Boot_0012>+[GetManaRegenBonus] Mana Per Second', -- See above, but for mana regen
        },
    },


    Mesh = '/meshes/items/chest/chest_mesh', -- (Mesh used for ingame animation. I am not sure what this one is so i couldnt  give you an example)


    Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2', -- (see above but this refers to the animation)


    MeshScale = 0.10, -- the size of the item in the animation


    Icon = 'NewIcons/Helm/Helm5', -- icon file used for the icon, this can be different for abilities an buffs but i recommend that they be the same as the item.

____________________________________________

Other functions that should be called here are:

    InventoryType = 'Achievement',  -  Can also be Clickables, havent tried inventory yet.


    Useable = true, - If you want the item to be able to be activated add this line

___________________________________________

Abilities can be called from outside an itemblueprint if necessary if you need i will go into this later. Also an ablity may be called by more than one item, more on this with in the use item section. Multiple abilities may be added to a single item

    Abilities = {


        AbilityBlueprint {
            Name = 'Item_Helm_010',

Name of the ability, abilities may be named differently to the item or even be present in a seperate part of the file or different file altogether as long as the are called correctly. I will try to add this in later.


            AbilityType = 'Quiet', (ie effects occur on when the ability is added to the unit)

Other ability types include 'Aura', TargetedUnit, WeaponProc, ArmorProc, Instant


            FromItem = 'Item_Helm_010',

Only required if you want the ability loaded upon equiping or using the item. If you use a function to call the ability this line is not required


            Icon = 'NewIcons/Helm/Helm5',

See earlier

*Note: this is a passive ability i will go into on use items etc in a bit. These add a number of new lines of code.

____________________________________________________

Ok as per abilities buffs may be called from outside the itemblueprint once again more on this later. Also multiple buffs may applied by the same item.


            Buffs = {
                BuffBlueprint {
                    Name = 'Item_Helm_010',

See previous, once again the buff name may be different to the item name


                    BuffType = 'GLYPHMAGIC',

Used for shared abilities, my understanding of this is limited


                    Debuff = false,

Whether the buff applies a negative effect that can be removed with dispel

                    EntityCategory = 'ALLUNITS',

Which units the buff is able to affect

                    Stacks = 'ALWAYS',

Whether the buff may stack or replace

                    Duration = -1,

Time the buff is active for (-1 is permanent).


                    Affects = {


                        MaxEnergy = {Add = 525, AdjustEnergy = false},


                        EnergyRegen = {Add = 4},
                    },

Affects, this is the meat of the buff and applies appropriate effects. I will list avaliable variables later (these can be found in hook/lua/sim/buffaffects.lua)

 

                }
            },
        }
    },
}

Ok so that was a passive item, now onto an on use item. I will bold the important parts of the code for these item types.

For on use items, you use the same Itemblueprint functions however a few more a required.

For example i will use Holy Symbol of Purity (/hook/lua/common/items/achievement_items.lua)

ItemBlueprint {
    Name = 'AchievementPure',
    DisplayName = '<LOC ITEM_Achievement_0012>Symbol of Purity',
    Description = '<LOC ITEM_Achievement_0013>Use: Purge all negative effects. Cannot use while stunned or frozen.',
    GetHealthBonus = function(self) return Buffs['AchievementPure_Buff'].Affects.MaxHealth.Add end,
    Tooltip = {
        Bonuses = {
            '<LOC ITEM_Chest_0003>+[GetHealthBonus] Health',
        },
    },
    Mesh = '/meshes/items/chest/chest_mesh',
    Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
    MeshScale = 0.10,
    Icon = '/NewIcons/AchievementRewards/HolysymbolofPurity',
    InventoryType = 'Achievement',
    GetMaxRange = function(self) return Ability['AchievementPure'].RangeMax end,
    GetEnergyCost = function(self) return Ability['AchievementPure'].EnergyCost end,
    GetCastTime = function(self) return Ability['AchievementPure'].CastingTime end,
    GetCooldown = function(self) return Ability['AchievementPure'].Cooldown end, - required for tooltip
    Useable = true, - allows activation of the item by clicking
    Abilities = {
        AbilityBlueprint {
            Name = 'AchievementPure',
            AbilityType = 'Instant', - required for casting, note that there still is a casting time
            AbilityCategory = 'USABLEITEM',
            TargetAlliance = 'Ally', - This tells the ability which alliance to allow targeting of. Fairly self explanatory. May be Ally, Enemy or Any
            Cooldown = 30,
            CastingTime = 0, - Above 0 for a non instant cast item
            CanCastWhileMoving = true, - may be false if you want casting to stop demigod movement
            EnergyCost = 0, - increase for item to cost mana on use
            NotSilenceable = true, - not required but can be used if you think that the item should not be silenceable
            InventoryType = 'Achievement',
            Audio = {
                 OnStartCasting = {Sound = 'Forge/ITEMS/snd_item_conjure',},
                 OnFinishCasting = {Sound = 'Forge/ITEMS/Achievement/snd_item_achievement_AchievementPure',},
                 OnAbortCasting = {Sound = 'Forge/ITEMS/snd_item_abort',},
             },
- sounds produced during casting

            OnStartAbility = function(self, unit, params)
                Buff.RemoveBuffsByDebuff(unit, true)
                AttachEffectsAtBone( unit, EffectTemplates.Items.Achievement.HolySymbolActivate, -2 )
            end,
- see further down for the function explinations. This is required however for activation of an effect on item use
            FromItem = 'AchievementPure',
            Icon = '/NewIcons/AchievementRewards/HolysymbolofPurity',
        },
        AbilityBlueprint {
            Name = 'AchievementPure_Buff',
            AbilityType = 'Quiet',
            FromItem = 'AchievementPure',
            Icon = 'NewIcons/Hand/Hand1',
            Buffs = {
                BuffBlueprint {
                    Name = 'AchievementPure_Buff',
                    BuffType = 'PURITYBUFF',
                    Debuff = false,
                    EntityCategory = 'ALLUNITS',
                    Stacks = 'ALWAYS',
                    Duration = -1,
                    Affects = {
                        MaxHealth = {Add = 250, AdjustHealth = true},
                    },
                },
            },
        }
    },
}

Ok so now you have an on use item, to apply a new ability or buff to the targeted unit you should change this code:

            OnStartAbility = function(self, unit, params)
                Buff.RemoveBuffsByDebuff(unit, true)
                AttachEffectsAtBone( unit, EffectTemplates.Items.Achievement.HolySymbolActivate, -2 )
            end,
 

Note that the current code removes debuffs by the debuff = true, tag. AttachEffects at bone plays an animation on use.

To apply new buffs following target aquisition replace the middle two lines with the following code (this is how you call abilities and buffs that are not part of the itemblueprint.)

Buff.ApplyBuff( target, 'BuffName01', unit )

To apply new abilities on target aquistion use

Abil.AddAbility(target, 'AbilityName01', true)

 

Note the part in brackets. Target may also be self, or where a loop is used, v. 'AbilityName01'/'BuffName01' are the names of the ability or buff you want applied. Finally Buffs require 'unit' as a target while abilities are enabled with a boolean.

Variables (buff affects):

So now you should be able to make both passive and items (if you guys want some more examples please ask!). So now i will go into the variables avalible for buff affects. So if we go back to the buff code:

                BuffBlueprint {
                    Name = 'AchievementPure_Buff',
                    BuffType = 'PURITYBUFF',
                    Debuff = false,
                    EntityCategory = 'ALLUNITS',
                    Stacks = 'ALWAYS',
                    Duration = -1,
                    Affects = {
                        MaxHealth = {Add = 250, AdjustHealth = true},
                    },

                },

 

The affects have been bolded because this causes the changes most items cause.

ie Maximum health will be increased by 250 and health pool will be adjusted by the maximum health amount.

Other variables include:

Health - health pool

MaxHealth- Total Health

Regen - Health regen

RateOfFire - Attack speed

DamageBonus - Auto Attack damage bonus

DamageRadius - applies cleave damage to radius

DamageSplash - similar to above not completely sure on the difference

MaxRadius - Weapon Range

MoveSlowCap - Minimum slow that may be applied

Interrupt - note this must be a boolean ( ie Interupt = (bool = true)) - this is pentinence interupt

Stun - think eribus mass charm effect. has interupt built in

Freeze - think tb frost nova. has interupt built in

MaxEnergy - Max Mana

Energy - Mana pool

EnergyRegen - Mana Regen Rate

SpellCostMult - adjust the energy cost of an ability

DamageReturn - returns a set amount of damage to the attacker on being autoattack

LifeSteal - as per name

Feedback - not sure on this one

CaptureTime - adjusts capture time on flags, mostly used for map setup

DeathPenaltyMult - death penalty time

DamageTakenMult - adjusts the amount of damage taken. Ie Bulkwark

WeaponsEnable - disables weapons, useful for animations

MetaRadius - Meta is small unit knockback, this defines the area around attacked unit which will be thrown

MetaAmount - This determines the damage multiplier applied to meta'ed infantry

Evasion - as per name

VisionRadius - Applies a vision radius around target or summoned unit (tracking bug)

Invincible - Oaks shield

Absorption - QOT shield

Armor - as per name

Cloak - Applies invisibilty

Silence - Works similar to interupt use (bool = true)

Ok i am off to bed. I will write up some function information later . Will also write up how to add new characters, units, textures. Teleport code and how to select units around target.

________________________________________________________________
Questions:

Before i start, it will still prbly take you about 20 hours to get farmiliar with the game code. Guides will just help your understanding once you understand how the lua interacts.

Ok onto the questions

1. Please explain why some close brackets have a comma.

Answer:

As far as i know, everything must have a comma unless it is closing a blueprint or is part of a function.

Please correct me if i am wrong.

2. Add effects to exsisting abilities --- Please Define. Do you mean show how targeting is done? To add different effects is already written in there the first part of the guide (see abilitytype).

Anyway since you wanted to know how to add interupt to spike wave i will do that in a bit (edit now post2)

Ability Types

Ok all of the below will require these functions added to the AbilityBlueprint:

 

TargetAlliance =  'Ally', 'Enemy', 'Any'  --Who you want to target

TargetCategory = 'HERO', 'MINION', 'ALLUNITS', 'STRUCTURES' and i think 'GRUNT' But this might be incorrect. There are a number of others but they should be the ones most ppl need.

 

Below is the specific code for each ability category

Aura

AbilityType = 'Aura'

AuraPulseTime =  3  How often you want to apply the buff to targets in radius.

AffectRadius = 1,  How big do you want the aura to be. 15 is the standard ranged AA distance

 

TargetedUnit - for auras you need these bits of code to the ability blueprint:

AbilityCategory = 'USABLEITEM',

TargetingMethod = 'AREAARGETED','ALLYSTRUCTURE', 'HOSTILETARGETED' - common ones. This one needs an individual discussion.

WeaponProc

Difficult to explain with comments. Effectively the following will apply the buff to the target 18% of the time when an AA lands.

            WeaponProcChance = 18,
            WeaponProcChanceRanged = 18,
            OnWeaponProc = function(self, unit, target, damageData)
                if not target:IsDead() then
                    Buff.ApplyBuff(target, 'AchievementDOT', unit)
                end
            end,

ArmorProc

Similar to weapon proc

            ArmorProcChance = 18,
            OnArmorProc = function(self, unit, target, damageData)
                if not target:IsDead() then
                    Buff.ApplyBuff(target, 'AchievementDOT', unit)
                end
            end,

Instant

            AbilityCategory = 'USABLEITEM',

            CastingTime = #,

            EnergyCost = #,

Where # is a number

Ok now to build a new ability for an example.

See example below as to how to change an ability to an item and add new buffs (post 2)

_______________________________________________________________

3.Add buff/debuff effects to exsisting abilities

See above

4. Which program should you use to edit lua

I am using SciTE. I was freeware and works well for me. even has a debugger!

 

Will continue further with the tutorial based on feedback and more questions.


Exx

____________________________________________________________

How to add items to the shop so you can try them out.

I will use my mod to do this there is a version of 1.8 beta avalible that you can grab from the ftp. Credit to mr rappard for the Item tree shop and achievement shop mods.

The advantage of this is that both have been combined and now function together so you can test your items as achivement or shop items, and there is free space in both shops. Ai will not pick the new items.

For achievement items:

Open Demigod\bindata\mods\FavorMod\UGBShop08_exx_ShopLayout.lua

Open Demigod\bindata\mods\FavorMod\hook\units\ug\b\ugbshop08\ugbshop08_exx_unit.bp

Code: c++
  1. #Scroll to here
  2.  
  3.                         #AchievementSwarm
  4.             AchievementSwarm = {
  5.                 Cost = 1,                                #NOTE: change this value to change cost of item. (Achivement shop will charge             achivement points)
  6.                 ItemBP = 'AchievementSwarm',
  7.         },
  8. #copy and paste
  9.                         #AchievementSwarm
  10.             AchievementSwarm = {
  11.                 Cost = 1,
  12.                 ItemBP = 'AchievementSwarm',
  13.         },                       
  14.                    #AchievementSwarm
  15.             AchievementSwarm = {
  16.                 Cost = 1,
  17.                 ItemBP = 'AchievementSwarm',
  18.         },
  19. #replace new shop item with your item name
  20.                         #AchievementSwarm
  21.             AchievementSwarm = {
  22.                 Cost = 1,
  23.                 ItemBP = 'AchievementSwarm',
  24.         },                 
  25.         #YourNewItem
  26.             YourNewItem= {
  27.                 Cost = 1,
  28.                 ItemBP = 'YourNewItem',
  29.         },

Save ugbshop08_exx_unit.bp

goto ugbshop08_exx_shoplayout.lua

[code = "c++"]

{  'AchievementSwarm',    '',},

# change to:

{  'AchievementSwarm',    'YourNewItemName',},

[/code]

Save

Your new item will now be in the forth tab bottom right corner.

For other shops:

goto the respective shop tab. And do as per achivement items.

Note The standard shop tabs can be found at: Demigod\bindata\mods\FavorMod\hook\units\ug\b\.

shop02Mod = chest

shop03Mod = gloves

shop04Mod = helm

shop05Mod = Artifact - (not sure if this tab works in this version)

shop06Mod = Trinket Shop

shop07Mod = Consumables

shop08Mod = dont use. (achivement items, may cause conflicts)

shop09Mod= General

shop10Mod= Boots

Cheers,

Exx

 

Locked Post 18 Replies
Search this post
Subscription Options


Reason for Karma (Optional)
Successfully updated karma reason!
February 17, 2010 4:59:58 AM from Demigod Forums Demigod Forums

This will be the example post. For Examples See here.

Spike Wave with interupt (as a favour item):

Step 1 Finding Spike Wave.

 First find spike wave. It can be found in dgdata/units/heroes/HQueen/HQueen_Abilities.lua

Spike Wave Code consists of (note this must be cut and pasted to achievement_items.lua)

Code: c++
  1. #################################################################################################################
  2. # CE: Spike Wave
  3. #################################################################################################################
  4. function SpikeWave(abil, unit, params)
  5.     local instigatorBp = unit:GetBlueprint()
  6.     local instigatorArmy = unit:GetArmy()
  7.     local direction = VNormal(params.Target.Position - unit:GetPosition())
  8.     local projectileData = {
  9.         data = {
  10.             Instigator = unit,
  11.             InstigatorBp = instigatorBp,
  12.             InstigatorArmy = instigatorArmy,
  13.             Amount = abil.DamageAmt,
  14.             Type = abil.DamageType,
  15.             DamageAction = abil.Name,
  16.             Radius = abil.AffectRadius,
  17.             DamageFriendly = false,
  18.             DamageSelf = false,
  19.             Group = "UNITS",
  20.             CanBeEvaded = false,
  21.             CanCrit = false,
  22.             CanBackfire = false,
  23.             CanDamageReturn = false,
  24.             CanMagicResist = true,
  25.             ArmorImmune = true,
  26.         },
  27.         direction = direction,
  28.         range = abil.RangeMax,
  29.     }
  30.     local proj = unit:CreateProjectile('/projectiles/Spike03/Spike03_proj.bp', 0, 1, 0, direction[1], 0, direction[3])
  31.     proj:SetVelocity(27)
  32.     proj:PassData(projectileData)
  33. end
  34. #################################################################################################################
  35. # Spike Wave I
  36. #################################################################################################################
  37. AbilityBlueprint {
  38.     Name = 'HQueenSpikeWave01',
  39.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  40.     Description = '<LOC ABILITY_Queen_0029>Queen of Thorns sends out a powerful wave of spikes, dealing [GetDamageAmt] damage to anyone skewered and slowing enemies by [GetSlowAmount]% for [GetDuration] seconds.',
  41.     AbilityType = 'TargetedArea',
  42.     TargetAlliance = 'Enemy',
  43.     TargetCategory = 'ALLUNITS - UNTARGETABLE',
  44.     EnergyCost = 750,
  45.     RangeMax = 20,
  46.     Cooldown = 10,
  47.     DamageAmt = 350,
  48.     DamageType = 'Spell',
  49.     AffectRadius = 5,
  50.     UISlot = 2,
  51.     HotKey = '2',
  52.     AbilityCategory = 'HQUEENUNPACKED',
  53.     CastingTime = 0.4,
  54.     CastAction = 'SpikeWave',
  55.     FollowThroughTime = 1.0,
  56.     GetDamageAmt = function(self) return math.floor( self.DamageAmt ) end,
  57.     GetSlowAmount = function(self) return math.floor( Buffs['HQueenSpikeWave01'].Affects.MoveMult.Mult * -100 ) end,
  58.     GetDuration = function(self) return ( Buffs['HQueenSpikeWave01'].Duration ) end,
  59.     OnStartAbility = function(self, unit, params)
  60.         SpikeWave( self, unit, params)
  61.     end,
  62.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  63.     Reticule = 'AoE_Spike_Wave',
  64. }
  65. BuffBlueprint {
  66.     Name = 'HQueenSpikeWave01',
  67.     BuffType = 'HQUEENSPIKEWAVEDEBUFF',
  68.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  69.     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  70.     Debuff = true,
  71.     CanBeDispelled = true,
  72.     Stacks = 'REPLACE',
  73.     Duration = 5,
  74.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  75.     Affects = {
  76.         MoveMult = {Mult = -0.15},
  77.     },
  78.     Effects = 'Slow03',
  79. }

Ok so CE: Spike Wave is the damage function of the AOE while Spike Wave 1 is the ability that references this function.

Step 2. Changing spike wave so it is an on use item. (Spike Wave 1)

Add ItemBlueprint. (here i will use 2 Way Mirror From my MOD

cut and paste

Code: c++
  1. ItemBlueprint {
  2.     Name = 'AchievementSwarm',
  3.     Description = '<LOC ITEM_Achievement_0584> Two Way Mirror',
  4.     DisplayName = '<LOC ITEM_Achievement_0585> Two Way Mirror',
  5.        Tooltip = {
  6.         Bonuses = {
  7.               '<LOC ITEM_Achievement_0623> Range : [GetAffectRadius]',
  8.             '<LOC ITEM_Achievement_0624> Lasts 20 Seconds and has 1000 Health',
  9.           },
  10.     },
  11.     GetEnergyCost = function(self) return Ability['AchievementST'].EnergyCost end,
  12.     GetAffectRadius = function(self) return Ability['DeployWardAura'].AffectRadius end,
  13.     GetCooldown = function(self) return Ability['AchievementST'].Cooldown end,
  14.      GetCastTime  = function(self) return Ability['AchievementST'].CastingTime end,
  15.     Mesh = '/meshes/items/chest/chest_mesh',
  16.     Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
  17.     MeshScale = 0.10,
  18.         Icon = 'NewIcons/Scroll/Scroll5',
  19.     InventoryType = 'Achievement',
  20.     Useable = true,

So now we have this:

Code: c++
  1. ItemBlueprint {
  2.     Name = 'AchievementSwarm',
  3.     Description = '<LOC ITEM_Achievement_0584> Two Way Mirror',
  4.     DisplayName = '<LOC ITEM_Achievement_0585> Two Way Mirror',
  5.        Tooltip = {
  6.         Bonuses = {
  7.               '<LOC ITEM_Achievement_0623> Range : [GetAffectRadius]',
  8.             '<LOC ITEM_Achievement_0624> Lasts 20 Seconds and has 1000 Health',
  9.           },
  10.     },
  11.     GetEnergyCost = function(self) return Ability['AchievementST'].EnergyCost end,
  12.     GetAffectRadius = function(self) return Ability['DeployWardAura'].AffectRadius end,
  13.     GetCooldown = function(self) return Ability['AchievementST'].Cooldown end,
  14.      GetCastTime  = function(self) return Ability['AchievementST'].CastingTime end,
  15.     Mesh = '/meshes/items/chest/chest_mesh',
  16.     Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
  17.     MeshScale = 0.10,
  18.         Icon = 'NewIcons/Scroll/Scroll5',
  19.     InventoryType = 'Achievement',
  20.     Useable = true,
  21. AbilityBlueprint {
  22.     Name = 'HQueenSpikeWave01',
  23.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  24.     Description = '<LOC ABILITY_Queen_0029>Queen of Thorns sends out a powerful wave of spikes, dealing [GetDamageAmt] damage to anyone skewered and slowing enemies by [GetSlowAmount]% for [GetDuration] seconds.',
  25.     AbilityType = 'TargetedArea',
  26.     TargetAlliance = 'Enemy',
  27.     TargetCategory = 'ALLUNITS - UNTARGETABLE',
  28.     EnergyCost = 750,
  29.     RangeMax = 20,
  30.     Cooldown = 10,
  31.     DamageAmt = 350,
  32.     DamageType = 'Spell',
  33.     AffectRadius = 5,
  34.     UISlot = 2,
  35.     HotKey = '2',
  36.     AbilityCategory = 'HQUEENUNPACKED',
  37.     CastingTime = 0.4,
  38.     CastAction = 'SpikeWave',
  39.     FollowThroughTime = 1.0,
  40.     GetDamageAmt = function(self) return math.floor( self.DamageAmt ) end,
  41.     GetSlowAmount = function(self) return math.floor( Buffs['HQueenSpikeWave01'].Affects.MoveMult.Mult * -100 ) end,
  42.     GetDuration = function(self) return ( Buffs['HQueenSpikeWave01'].Duration ) end,
  43.     OnStartAbility = function(self, unit, params)
  44.         SpikeWave( self, unit, params)
  45.     end,
  46.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  47.     Reticule = 'AoE_Spike_Wave',
  48. }
  49. BuffBlueprint {
  50.     Name = 'HQueenSpikeWave01',
  51.     BuffType = 'HQUEENSPIKEWAVEDEBUFF',
  52.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  53.     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  54.     Debuff = true,
  55.     CanBeDispelled = true,
  56.     Stacks = 'REPLACE',
  57.     Duration = 5,
  58.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  59.     Affects = {
  60.         MoveMult = {Mult = -0.15},
  61.     },
  62.     Effects = 'Slow03',
  63. }

Step 3. Altering the code for our item

The important things to do here:

Change the item name (must be a unique identifier) i am going to call this AchievementSpikeWaveTutorial

Change the ability name (again changed to AchievementSpikeWaveTutorial)

Change Buff name (again, changed to AchievementSpikeWaveTutorial --We will need to add another buff later)

Change Buff Type = 'AchievementSpikeWaveTutorial'

Add Abilities = {} to add the ability to the item

Remove the following functions from the ability blueprint (they are only required for demigod abilites)

    UISlot = 2,
    HotKey = '2',
    AbilityCategory = 'HQUEENUNPACKED',

Reduce energy cost to 0 (this is an item after all lol)

Increased cooldown to 30

Add FromItem= 'AchievementSpikeWaveTutorial' to the ability

We now have:

Code: c++
  1. #################################################################################################################
  2. # Spike Wave I - Tutorial
  3. #################################################################################################################
  4. ItemBlueprint {
  5.     Name = 'AchievementSpikeWaveTutorial',
  6.     Description = '<LOC ITEM_Achievement_0584> Two Way Mirror',
  7.     DisplayName = '<LOC ITEM_Achievement_0585> Two Way Mirror',
  8.        Tooltip = {
  9.         Bonuses = {
  10.               '<LOC ITEM_Achievement_0623> Range : [GetAffectRadius]',
  11.             '<LOC ITEM_Achievement_0624> Lasts 20 Seconds and has 1000 Health',
  12.           },
  13.     },
  14.     GetEnergyCost = function(self) return Ability['AchievementST'].EnergyCost end,
  15.     GetAffectRadius = function(self) return Ability['DeployWardAura'].AffectRadius end,
  16.     GetCooldown = function(self) return Ability['AchievementST'].Cooldown end,
  17.      GetCastTime  = function(self) return Ability['AchievementST'].CastingTime end,
  18.     Mesh = '/meshes/items/chest/chest_mesh',
  19.     Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
  20.     MeshScale = 0.10,
  21.         Icon = 'NewIcons/Scroll/Scroll5',
  22.     InventoryType = 'Achievement',
  23.     Useable = true,
  24.     Abilities = {
  25. AbilityBlueprint {
  26.     Name = 'AchievementSpikeWaveTutorial',
  27.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  28.     Description = '<LOC ABILITY_Queen_0029>Queen of Thorns sends out a powerful wave of spikes, dealing [GetDamageAmt] damage to anyone skewered and slowing enemies by [GetSlowAmount]% for [GetDuration] seconds.',
  29.     AbilityType = 'TargetedArea',
  30.     TargetAlliance = 'Enemy',
  31.     TargetCategory = 'ALLUNITS - UNTARGETABLE',
  32.     EnergyCost = 0,
  33.     RangeMax = 20,
  34.     Cooldown = 30,
  35.     DamageAmt = 350,
  36.     FromItem = 'AchievementSpikeWaveTutorial',
  37.     DamageType = 'Spell',
  38.     AffectRadius = 5,
  39.     CastingTime = 0.4,
  40.     CastAction = 'SpikeWave',
  41.     FollowThroughTime = 1.0,
  42.     GetDamageAmt = function(self) return math.floor( self.DamageAmt ) end,
  43.     GetSlowAmount = function(self) return math.floor( Buffs['HQueenSpikeWave01'].Affects.MoveMult.Mult * -100 ) end,
  44.     GetDuration = function(self) return ( Buffs['HQueenSpikeWave01'].Duration ) end,
  45.     OnStartAbility = function(self, unit, params)
  46.         SpikeWave( self, unit, params)
  47.     end,
  48.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  49.     Reticule = 'AoE_Spike_Wave',
  50. }
  51. }
  52. BuffBlueprint {
  53.     Name = 'AchievementSpikeWaveTutorial01',
  54.     BuffType = 'AchievementSpikeWaveTutorial',
  55.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  56.     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  57.     Debuff = true,
  58.     CanBeDispelled = true,
  59.     Stacks = 'REPLACE',
  60.     Duration = 5,
  61.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  62.     Affects = {
  63.         MoveMult = {Mult = -0.15},
  64.     },
  65.     Effects = 'Slow03',
  66. }

Ok now we need to adjust the spikewave function (CE: Spike Wave)

First we need to rename it. i will use 'SpikeWaveFunction'

Then we need to make the function refer to the item (it currently refers to the ability.)

 

Code: c++
  1. function SpikeWave(abil, unit, params)

is changed to

Code: c++
  1. function SpikeWaveFunction(self, unit, params)

Repeat this will all abil referals.

Note that self refers to the item that called the function.

Ok so now we have:

 

Code: c++
  1. #################################################################################################################
  2. # CE: Spike Wave Damage Function - Tutorial
  3. #################################################################################################################
  4. function SpikeWaveFunction(self, unit, params)
  5.     local instigatorBp = unit:GetBlueprint()
  6.     local instigatorArmy = unit:GetArmy()
  7.     local direction = VNormal(params.Target.Position - unit:GetPosition())
  8.     local projectileData = {
  9.         data = {
  10.             Instigator = unit,
  11.             InstigatorBp = instigatorBp,
  12.             InstigatorArmy = instigatorArmy,
  13.             Amount = self.DamageAmt,
  14.             Type = self.DamageType,
  15.             DamageAction = self.Name,
  16.             Radius = self.AffectRadius,
  17.             DamageFriendly = false,
  18.             DamageSelf = false,
  19.             Group = "UNITS",
  20.             CanBeEvaded = false,
  21.             CanCrit = false,
  22.             CanBackfire = false,
  23.             CanDamageReturn = false,
  24.             CanMagicResist = true,
  25.             ArmorImmune = true,
  26.         },
  27.         direction = direction,
  28.         range = self.RangeMax,
  29.     }
  30.     local proj = unit:CreateProjectile('/projectiles/Spike03/Spike03_proj.bp', 0, 1, 0, direction[1], 0, direction[3])
  31.     proj:SetVelocity(27)
  32.     proj:PassData(projectileData)
  33. end
  34. #################################################################################################################
  35. # Spike Wave I - Tutorial
  36. #################################################################################################################
  37. ItemBlueprint {
  38.     Name = 'AchievementSpikeWaveTutorial',
  39.     Description = '<LOC ITEM_Achievement_0584> Two Way Mirror',
  40.     DisplayName = '<LOC ITEM_Achievement_0585> Two Way Mirror',
  41.        Tooltip = {
  42.         Bonuses = {
  43.               '<LOC ITEM_Achievement_0623> Range : [GetAffectRadius]',
  44.             '<LOC ITEM_Achievement_0624> Lasts 20 Seconds and has 1000 Health',
  45.           },
  46.     },
  47.     GetEnergyCost = function(self) return Ability['AchievementST'].EnergyCost end,
  48.     GetAffectRadius = function(self) return Ability['DeployWardAura'].AffectRadius end,
  49.     GetCooldown = function(self) return Ability['AchievementST'].Cooldown end,
  50.      GetCastTime  = function(self) return Ability['AchievementST'].CastingTime end,
  51.     Mesh = '/meshes/items/chest/chest_mesh',
  52.     Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
  53.     MeshScale = 0.10,
  54.         Icon = 'NewIcons/Scroll/Scroll5',
  55.     InventoryType = 'Achievement',
  56.     Useable = true,
  57.     Abilities = {
  58. AbilityBlueprint {
  59.     Name = 'AchievementSpikeWaveTutorial',
  60.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  61.     Description = '<LOC ABILITY_Queen_0029>Queen of Thorns sends out a powerful wave of spikes, dealing [GetDamageAmt] damage to anyone skewered and slowing enemies by [GetSlowAmount]% for [GetDuration] seconds.',
  62.     AbilityType = 'TargetedArea',
  63.     TargetAlliance = 'Enemy',
  64.     TargetCategory = 'ALLUNITS - UNTARGETABLE',
  65.     EnergyCost = 0,
  66.     RangeMax = 20,
  67.     Cooldown = 30,
  68.     DamageAmt = 350,
  69.     DamageType = 'Spell',
  70.     FromItem = 'AchievementSpikeWaveTutorial',
  71.     AffectRadius = 5,
  72.     CastingTime = 0.4,
  73.     CastAction = 'SpikeWave',
  74.     FollowThroughTime = 1.0,
  75.     GetDamageAmt = function(self) return math.floor( self.DamageAmt ) end,
  76.     GetSlowAmount = function(self) return math.floor( Buffs['AchievementSpikeWaveTutorial'].Affects.MoveMult.Mult * -100 ) end,
  77.     GetDuration = function(self) return ( Buffs['AchievementSpikeWaveTutorial'].Duration ) end,
  78.     OnStartAbility = function(self, unit, params)
  79.         SpikeWaveFunction( self, unit, params)
  80.     end,
  81.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  82.     Reticule = 'AoE_Spike_Wave',
  83. }
  84. }
  85. }
  86. BuffBlueprint {
  87.     Name = 'AchievementSpikeWaveTutorial',
  88.     BuffType = 'AchievementSpikeWaveTutorial',
  89.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  90.     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  91.     Debuff = true,
  92.     CanBeDispelled = true,
  93.     Stacks = 'REPLACE',
  94.     Duration = 5,
  95.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  96.     Affects = {
  97.         MoveMult = {Mult = -0.15},
  98.     },
  99.     Effects = 'Slow03',
  100. }

This is now a working item.

Now to add a new buff which adds an interupt.

Step 4: Adding new buff to the item.

First we need to write the buff. We want an interupt that lasts 1.5 seconds.

So copy pasted from my mod, static orb

Code: c++
  1.                 BuffBlueprint {
  2.                     Name = 'AchievementOrbA',
  3.                     DisplayName = '<LOC ITEM_Achievement_00443>Static Orb',
  4.                     Description = '<LOC ITEM_Achievement_00442> Stun',
  5.                     BuffType = 'AchievementOrbA',
  6.                     Debuff = true,
  7.                     CanBeDispelled = true,
  8.                     Duration = 1.5,
  9.                     ArmorImmune = true,
  10.                     Stacks = 'ALWAYS',
  11.                     Icon = '../../../../../mods/Favormod/Icons/Static_Orb',
  12.                     Affects = {
  13.                         Stun = {Add = 0},
  14.                         Health = {MaxHealthPercentlvl = 1},
  15.                     },
  16.                     Effects = 'Slow01',
  17.                     EffectsBone = -2,
  18.                 },

Now i will change the icon to match. Remove the damage and change the names. Also change the display name and description name and buff type. Also remove the armorImmune code and change stacks to ignore. Change the duration to 1.5 (the length of our stun).

so now we have

Code: c++
  1.                 BuffBlueprint {
  2.                    Name = 'AchievementSpikeWaveTutorialA',
  3.                    DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  4.                     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  5.                     BuffType = 'AchievementSpikeWaveTutoriaAl',
  6.                     Debuff = true,
  7.                     CanBeDispelled = true,
  8.                     Duration = 1.5,
  9.                     Stacks = 'IGNORE',
  10.                     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  11.                     Affects = {
  12.                         Stun = {Add = 0},
  13.                     },
  14.                     Effects = 'Slow01',
  15.                     EffectsBone = -2,
  16.                 }

We now need to make it so this buff is applied on damage. As we already have a buff that is applied by spike wave, lets piggyback off that.

to do this i will use the following function:

    OnBuffAffect = function(self, unit, params)
     Buff.ApplyBuff(unit, 'AchievementSpikeWaveTutorialA', unit)
     end

This is added to the already called buff. So our final Item will perform spike wave and stun all units that are hit by the AOE.

Code: c++
  1. #################################################################################################################
  2. # CE: Spike Wave Damage Function - Tutorial
  3. #################################################################################################################
  4. function SpikeWaveFunction(self, unit, params)
  5.     local instigatorBp = unit:GetBlueprint()
  6.     local instigatorArmy = unit:GetArmy()
  7.     local direction = VNormal(params.Target.Position - unit:GetPosition())
  8.     local projectileData = {
  9.         data = {
  10.             Instigator = unit,
  11.             InstigatorBp = instigatorBp,
  12.             InstigatorArmy = instigatorArmy,
  13.             Amount = self.DamageAmt,
  14.             Type = self.DamageType,
  15.             DamageAction = self.Name,
  16.             Radius = self.AffectRadius,
  17.             DamageFriendly = false,
  18.             DamageSelf = false,
  19.             Group = "UNITS",
  20.             CanBeEvaded = false,
  21.             CanCrit = false,
  22.             CanBackfire = false,
  23.             CanDamageReturn = false,
  24.             CanMagicResist = true,
  25.             ArmorImmune = true,
  26.         },
  27.         direction = direction,
  28.         range = self.RangeMax,
  29.     }
  30.     local proj = unit:CreateProjectile('/projectiles/Spike03/Spike03_proj.bp', 0, 1, 0, direction[1], 0, direction[3])
  31.     proj:SetVelocity(27)
  32.     proj:PassData(projectileData)
  33. end
  34. #################################################################################################################
  35. # Spike Wave I - Tutorial
  36. #################################################################################################################
  37. ItemBlueprint {
  38.     Name = 'AchievementSpikeWaveTutorial',
  39.     Description = '<LOC ITEM_Achievement_0584> Two Way Mirror',
  40.     DisplayName = '<LOC ITEM_Achievement_0585> Two Way Mirror',
  41.        Tooltip = {
  42.         Bonuses = {
  43.               '<LOC ITEM_Achievement_0623> Range : [GetAffectRadius]',
  44.             '<LOC ITEM_Achievement_0624> Lasts 20 Seconds and has 1000 Health',
  45.           },
  46.     },
  47.     GetEnergyCost = function(self) return Ability['AchievementST'].EnergyCost end,
  48.     GetAffectRadius = function(self) return Ability['DeployWardAura'].AffectRadius end,
  49.     GetCooldown = function(self) return Ability['AchievementST'].Cooldown end,
  50.      GetCastTime  = function(self) return Ability['AchievementST'].CastingTime end,
  51.     Mesh = '/meshes/items/chest/chest_mesh',
  52.     Animation = '/meshes/items/chest/Animations/chest_Idle_anim.gr2',
  53.     MeshScale = 0.10,
  54.         Icon = 'NewIcons/Scroll/Scroll5',
  55.     InventoryType = 'Achievement',
  56.     Useable = true,
  57.     Abilities = {
  58. AbilityBlueprint {
  59.     Name = 'AchievementSpikeWaveTutorial',
  60.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  61.     Description = '<LOC ABILITY_Queen_0029>Queen of Thorns sends out a powerful wave of spikes, dealing [GetDamageAmt] damage to anyone skewered and slowing enemies by [GetSlowAmount]% for [GetDuration] seconds.',
  62.     AbilityType = 'TargetedArea',
  63.     TargetAlliance = 'Enemy',
  64.     TargetCategory = 'ALLUNITS - UNTARGETABLE',
  65.     EnergyCost = 0,
  66.     RangeMax = 20,
  67.     Cooldown = 30,
  68.     DamageAmt = 350,
  69.     DamageType = 'Spell',
  70.     FromItem = 'AchievementSpikeWaveTutorial',
  71.     AffectRadius = 5,
  72.     CastingTime = 0.4,
  73.     CastAction = 'SpikeWave',
  74.     FollowThroughTime = 1.0,
  75.     GetDamageAmt = function(self) return math.floor( self.DamageAmt ) end,
  76.     GetSlowAmount = function(self) return math.floor( Buffs['AchievementSpikeWaveTutorial'].Affects.MoveMult.Mult * -100 ) end,
  77.     GetDuration = function(self) return ( Buffs['AchievementSpikeWaveTutorial'].Duration ) end,
  78.     OnStartAbility = function(self, unit, params)
  79.         SpikeWaveFunction( self, unit, params)
  80.     end,
  81.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  82.     Reticule = 'AoE_Spike_Wave',
  83. }
  84. }
  85. }
  86. BuffBlueprint {
  87.     Name = 'AchievementSpikeWaveTutorial',
  88.     BuffType = 'AchievementSpikeWaveTutorial',
  89.     DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  90.     Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  91.     Debuff = true,
  92.     CanBeDispelled = true,
  93.     Stacks = 'REPLACE',
  94.     Duration = 5,
  95.     Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  96.     Affects = {
  97.         MoveMult = {Mult = -0.15},
  98.     },
  99.     Effects = 'Slow03',
  100.     OnBuffAffect = function(self, unit, params)
  101.      Buff.ApplyBuff(unit, 'AchievementSpikeWaveTutorialA', unit)
  102.      end
  103. }
  104. BuffBlueprint {
  105.         Name = 'AchievementSpikeWaveTutorialA',
  106.         DisplayName = '<LOC ABILITY_Queen_0028>Spike Wave I',
  107.         Description = '<LOC ABILITY_HGSA01_0014>Movement Speed reduced.',
  108.         BuffType = 'AchievementSpikeWaveTutorialA',
  109.         Debuff = true,
  110.         CanBeDispelled = true,
  111.         Duration = 1.5,
  112.         Stacks = 'REPLACE',
  113.         Icon = '/dgqueenofthorns/NewQueenSpikeWave01',
  114.         Affects = {
  115.             Stun = {Add = 0},
  116.         },
  117.         Effects = 'Slow01',
  118.         EffectsBone = -2,
  119. }

Hope that helps . Any questions feel free to ask!

 

 

 

 

 

Reason for Karma (Optional)
Successfully updated karma reason!
February 17, 2010 5:00:11 AM from Demigod Forums Demigod Forums

NT - sorry i might need the space

Reason for Karma (Optional)
Successfully updated karma reason!
February 17, 2010 11:43:48 AM from Demigod Forums Demigod Forums

Please explain why some close brackets have a comma, the online LUA doc is mostly useless for learning LUA to actually do things.

{
      {
      },
{

If you really want to do something additonally useful to help the modding scene

1) show us how to properly add an interrupt effect to an ability. (you could do this with Spike Wave, as it is a single effect, multi-target spell in its original form). The reason, simply adding interrupt to the spikewave effect will cause a 5 seconds interrupt. I'm thinking you need to create a buffs ={} section to encapsulate the snare effect while adding a second blueprint for the interrupt effect to .1 duration, but I can't get it to work without breaking something.

2) How to create a new buff or debuff effect complete with icon in the effects bar. (haven't got this far, still stuck on #1) But it would be useful to know how to add a duration buff/debuff effect with icon that shows up on the effects bar.

Those 2 items combied with this excellent guide would make it easier for people to get started who don't necessarily want to spend 20 hours learning the basics of LUA and how it releates to DGs data files.

eg: A perfect intro guide would have

1) Item basics (already done)
2) Add effects to exsisting abilities
3) Add buff/debuff effects to exsisting abilities

 

Also, what IDE should you use for LUA on windows? Using VS sucks as the language plugin for it is extremely limited.

Reason for Karma (Optional)
Successfully updated karma reason!
February 18, 2010 7:37:13 AM from Demigod Forums Demigod Forums

Bump. Questions answered and OP and example added/updated

Note that spike wave is difficult to add new buffs too as it does not select a target directly and the AOE code can be troublesome to work with.

Also if your modding make sure that you have the game in windowed mode with the debugger console on. It makes modding much more simple.

Also note that i did not change tooltips and tooltip name in my example (just because it is time consuming). An icon will appear in the demigod tray for both the slow and the stun tho.

 

Reason for Karma (Optional)
Successfully updated karma reason!
February 18, 2010 8:37:17 AM from Demigod Forums Demigod Forums

Awesome, thanks.

Reason for Karma (Optional)
Successfully updated karma reason!
February 19, 2010 2:13:12 AM from Demigod Forums Demigod Forums

Cheers. Let me know if you create any cool items!

 

Updated OP with tutorial on adding items to the shop.

 

Reason for Karma (Optional)
Successfully updated karma reason!
March 1, 2010 1:58:19 AM from Demigod Forums Demigod Forums

is anyone using this? is it worth me continuing to update?

Reason for Karma (Optional)
Successfully updated karma reason!
March 1, 2010 2:03:59 AM from Demigod Forums Demigod Forums

Yes, I have been reading it. Could you perhaps do one on what it takes to create a Demigod ability? I don't know what all files are involved. I've been trying to convert item abilities such as the Cloak of Invisibility and Orb of Defiance into Demigod abilities, but they are coded differently, and I'm not sure how to convert them.

EDIT: Also, do you know what "Benign" means? I've seen it in the LUA several times.

Reason for Karma (Optional)
Successfully updated karma reason!
April 21, 2010 3:58:24 PM from Demigod Forums Demigod Forums

I am using it.

I will probably relase a mod at some point.

Eventually DG will be like total anniilation for me. I will play it in offline mode occassionally with my own better AI and balance, like I did with my warmonger mod for Total Annihilation.

Reason for Karma (Optional)
Successfully updated karma reason!
June 2, 2010 2:13:12 AM from Demigod Forums Demigod Forums

From the other tutorial thread:

Quoting OMG_Teseer,
I read DeadMG's post on 'Hooking' and I didnt quite understand it. Is there anywhere I could find more info on that?

I realize I'm about five months late with this, but this game has been out for more than a year and I'm seeing a whole lot of unnecessary overriding in literally all of the sim-side mods that I download, which is making almost all mods not play nice with each other.  For example, if you use the method detailed in this thread to rebalance an item (or any other type of game object), it will be completely incompatible with:

  • Future versions of Demigod that change this item's function code
  • The Uberfix or any other community fix patches that change this item's function code
  • Other mods that change any aspect of the item other than those you changed

Obviously mods that attempt to change the same values in the same items/abilities/buffs/units will always be incompatible, and some balance mod combinations will have this issue no matter what, but this is not always the case.

Even worse, I see a lot of people overriding ENTIRE FILES to change one or two values.  This will obliterate any other mods' changes to anything else in the file, in addition to what is mentioned above.  Yikes.

 

There is actually a very simple way around this!

First, understand that the blueprints for all items, abilities, buffs, army bonuses, and powerups are stored as simple Lua tables.  Unit, projectile, emitter, effect, etc blueprints are as well, but those are stored a bit differently and we can't directly access them using the same method.  Their method is actually even simpler, but for purposes of clarity, what I'm about to explain only directly applies to the above underlined blueprint types, all found in files within the /lua/common directory.

When you see an ItemBlueprint {} call, that's running a global function (quite unsurprisingly called 'ItemBlueprint') and passing it a table (the part in brackets: {}) containing the blueprint as the first argument.  That function takes the blueprint table, grabs the Name variable from it, and stuffs the whole blueprint into a globally accessible table (in this case, called 'Items').  The Name variable is used as the item's 'key'-- your means of accessing it-- in this global item table.  This means that we only need one line of code in the same file that the blueprint was originally in to change one attribute of the blueprint!

Now, where things get tricky in Demigod is that an ItemBlueprint call will also contain AbilityBlueprint and BuffBlueprint calls.  These are actually executed during the ItemBlueprint call, creating separate blueprints stored in different global tables, leaving only a string containing their name in place of the AbilityBlueprint or BuffBlueprint call.  So if you were to output the contents of an Item Blueprint to the log, you would not see the whole tree of ability and buff blueprints it contains - only the ability names!  And if you looked at the contents of a stored AbilityBlueprint, you would only see the buff blueprint names.

Why am I explaining all of this?  So you understand that we'll be accessing three different global tables to change different components of the same item.

 

I'll assume that you, the casual modder, are already bored by my explanation.  You just wanna tweak some stats, switch a buff here and there, or maybe this is all just blah blah blah and you learn by example and not exposition-- I dig.  So I'll toss out some quick examples, using the Holy Symbol of Purity item, from /lua/common/Items/Achievement_Items.lua.

 

Changing a value in the Item portion of the blueprint:

Code: c++
  1. Items.AchievementPure.Description = 'This is my new item description.'

or

Code: c++
  1. Items['AchievementPure']['Description'] = 'This is my new item description.'

Please notice that this line does not end in a comma!  In Lua, commas are only used to separate elements (variables or sub-tables) within a table.  What we're writing here is an actual line of Lua code that directly accesses a variable ('Description') inside a table ('AchievementPure') inside a global table ('Items') containing all blueprints for this type of object.

These two lines are the same thing - in Lua, you can access an element of a table with either a period or by enclosing it in brackets as a string.  Use whichever syntax you feel like for this particular application, though I prefer the former for readability when you don't need to pass it a string variable.

 

Changing a value in the Ability portion of the blueprint:

Code: c++
  1. Ability.AchievementPure.Cooldown = 20

Again, no comma.  Notice we had to access the global table Ability to change this; we can't do Items.AchievementPure.Abilities.etcetc because that table contains nothing but the abilities' names once the blueprint has been created by the original version of the file (or any subsequent, ill-advised copy-paste of the whole item blueprint).

Just because the ability name in this example is the same as the item name doesn't mean you should always assume this.  Look in the original file and sure you're using the ability's Name here.  Not every ability and item are consistently name, and some items (such as this one) have more than one ability, and abilities often have more than one buff.  So pay attention to their names!

Also, notice how the name of the global table is 'Ability', not 'Abilities'.  Some of the global table names are plural, some of them aren't.  Like I just said, not everything is gonna be perfectly consistent.  I'll list all of the global table names in a minute, but you can find them at the top of the object type's blueprint file in /lua/system, in this case AbilityBlueprints.lua.

 

Changing a value in the Buff portion of the blueprint:

Code: c++
  1. Buffs.AchievementPure_Buff.Affects.MaxHealth.Add = 200

or, if you want to change the buff method:

 

Code: c++
  1. Buffs.AchievementPure_Buff.Affects.MaxHealth.Add = nil
  2. Buffs.AchievementPure_Buff.Affects.MaxHealth.Mult = 0.1

 

or, if you want to change the buff type entirely:

Code: c++
  1. Buffs.AchievementPure_Buff.Affects.MaxHealth = nil
  2. Buffs.AchievementPure_Buff.Affects.Regen = {Add = 10}

Notice how we have to nil out the previous buff affect when we're switching methods or types, otherwise you'll end up with both the original buff and the buff you're adding.

Also notice how we can't just define Affects.Regen.Add, since the 'Regen' table doesn't yet exist in that buff's Affects table.  If we try to define or access a variable within a non-existent table, the game's Lua interpreter will complain loudly and your entire file will not be imported.

 

This post is getting really long, but to cap it off, I need to do at least one or two more complete examples from which you can figure out how to make significant shifts in object functionality without having to overwrite the entire blueprint.  In the example above, switching  from a max health bonus to a regen bonus looks simple, except, you need to do a bit more than switch the buff affect type if you want the tooltip to be complete and accurate-- you also need to change the bonus string and add a function to report the new bonus.

 

Changing from a maxhealth bonus to a regen bonus:

Code: c++
  1. Buffs.AchievementPure_Buff.Affects.MaxHealth = nil
  2. Buffs.AchievementPure_Buff.Affects.Regen = {Add = 10}
  3. table.removeByValue(Items.AchievementPure.Tooltip.Bonuses, '<LOC ITEM_Chest_0003>+[GetHealthBonus] Health')
  4. table.insert(Items.AchievementPure.Tooltip.Bonuses, '+[GetRegenBonus] Health Per Second')
  5. Items.AchievementPure.GetRegenBonus = function(self) return Buffs['AchievementPure_Buff'].Affects.Regen.Add end

The first two lines are as above, changing the actual buff affect type.  The next two lines are the safest way to alter a table like Bonuses that won't step on the toes of other mods: table.removeByValue searches all keys in a table for the exact value (in this case a string) provided.  The original blueprint for this item only has one variable in its Bonuses table, which we could switch with one line with something like:

Code: c++
  1. Items.AchievementPure.Tooltip.Bonuses[1] = '+[GetRegenBonus] Health Per Second'

..which accomplishes the same thing.  Since any other mod that changed key 1 in the Bonuses table would probably conflict with ours, this is generally an ok and simpler way than using table manipulator functions, but I provided the first method as the best way to deal with an item that has many bonuses, of which you may only want to modify one.

The last line adds a small function called GetRegenBonus to the item's blueprint, which is then called by the bonus text we just added.  Notice that it just returns the same buff value that we just added, in the same place.  If the buff value you're accessing is a floating point value (i.e. has a fraction) and/or needs to be scaled up (e.g. an 0.15 modifier that you want to display as 15%), there are plenty of examples in GPG's code of how to format this mini-function's output using math.floor, etc.

When removing a bonus from an item, don't worry about removing the old display function (in this case, GetHealthBonus).  It's harmless on its own, and won't interfere with anything.

 

Oh, right, I said I was going to list the names of global blueprint tables.  Pretty self-explanatory:

Ability

ArmyBonuses

Buffs

Items

Loot

PowerUps

 

The rest of what you need to know to mod these blueprints is already in the original post-- props to Exxcentric for writing it up.  I'm not trying to step on any toes here or point fingers and say 'you're doing it wrong', I just want to help new modders bridge the gap between making a working mod, and making a working mod that works with other mods.

 

 

That's all I've got for now.  I don't know how often I'll be checking the forums, but if anyone has any questions about how to safely and non-destructively hook anything in Demigod, please post them.  I don't know a lot about the game's specific structure yet, but I'm no stranger to the Moho engine, and I enjoy a challenge.

Reason for Karma (Optional)
Successfully updated karma reason!
June 2, 2010 4:11:53 AM from Demigod Forums Demigod Forums

So I'm one of the worst contributors to modding delinquency for Demigods. I apologize. To be fair most of what I've submitted to the public has been in complete destructive obliterating file overriding for easier understanding of the code.

So, it took me a while, but I finally got the syntax to work for adding EffectTemplates.

Directly replacing a template was straightforward:

Code: c++
  1. do
  2. EffectTemplates.Buff.Bramble01.Small = '/mods/EffectExample/hook/effects/emitters/buffs/bramble/b_brm2_01_rings_h_emit.bp'
  3. end

However, when it came to adding a new template, the previous didn't work. I eventually found this:

Code: c++
  1. do
  2. EffectTemplates.Buff['Bramble02'] = {
  3.     Small =  {
  4.         '/mods/EffectExample/hook/effects/emitters/buffs/bramble/b_brm2_01_rings_h_emit.bp',
  5.         },
  6.     }
  7. end

In your previous (very useful btw, I wish you'd made it a while ago) post, you mention that you can't add new keys in the same way as you would replace old keys, but then skip ahead. How bad is it to add keys like this and is there a better method for it?

Reason for Karma (Optional)
Successfully updated karma reason!
June 2, 2010 6:05:32 AM from Demigod Forums Demigod Forums

Oh, no, you can.  You just can't add new keys to a table that doesn't exist yet.  It isn't 'bad' or 'good', it simply won't work if you try to access an element of a non-existent table, and your entire file won't parse.

For example:

Code: c++
  1. EffectTemplates.Buff.Bramble02 = { Small = {} }

as you have in your second block of code is just fine.  However, you'll get a parse error if 'Bramble02' doesn't exist yet, and you try:

Code: c++
  1. EffectTemplates.Buff.Bramble02.Small = {}

In other words, you can't define/declare variables or subtables within a table until the table itself is declared.  Of course, you can do this in one fell swoop by simply populating the table in your declaration, which is exactly what you're doing in your second example (and in my first example).  However, that table can't include actual lua code, only valid table elements: variables, tables, and functions (which are more or less tables in Lua).

 

So if you needed to create a new subtable within a table, and assign a value to it via a function, this necessitates two steps:

Code: c++
  1. EffectTemplates.Buff.Bramble02 = {}
  2. EffectTemplates.Buff.Bramble02.Small = MyFunction()

 

Make sense?  I'm not actually a programmer by trade, and everything I know I learned from various games' C pseudocode and FA's Lua, so I don't always have a solid grasp of the terminology needed to clearly explain things like this.  Of course, Lua complicates matters by being so blase about variable declaration and typing (which is fantastically convenient once you have a good handle on it).

Reason for Karma (Optional)
Successfully updated karma reason!
June 2, 2010 6:44:49 AM from Demigod Forums Demigod Forums

I may as well briefly touch on non-destructive unit/projectile/emitter/beam blueprint merging, since I guess the DG community never got that memo either.  The good news is that this is incredibly simple by comparison - you can literally stick all of these types of blueprint merges into one file in your mod, and you can use the same table structure that's found in the original blueprint.

For units, just create a file anywhere in your mod folder called something like mod_units.bp.  The name isn't terribly important, as the mod code searches your entire mod folder and executes any .bp files it finds, in the blueprint loading/RuleInit.lua state.  So you can't stick just any code in this file-- well, you can, but it will be working within the limitations of this special state and won't have access to a host of other normally global things, like the tables in the above post.

Example - boosting DA's base speed to 6.3:

Code: c++
  1. UnitBlueprint {
  2.     BlueprintId = "hdemon",
  3.     Merge = true,
  4.     Physics = {
  5.         MaxSpeed = 6.3,
  6.     },
  7. }

Same structure as the original blueprint file.  The key difference is that it needs a BlueprintId or Source, to specify which blueprint this UnitBlueprint call is referencing.  It also needs Merge = true, so that it knows to not overwrite the original blueprint with this very incomplete stub.  If it isn't obvious, Merge means that anything not specified in this blueprint fragment is left intact in the original blueprint.

BlueprintId for units is their original blueprint folder name in lowercase, e.g. 'hdemon' for DA, 'hgsa01' for Regulus, 'hoak' for Oak, etc.

 

For a projectile example, here's all the Uberfix needs to correct TB's projectile issues:

Code: c++
  1. --TB weapon fireball
  2. ProjectileBlueprint {
  3.     BlueprintId = "/projectiles/fireball01/fireball01_proj.bp",
  4.     Merge = true,
  5.     Physics = {
  6.         Acceleration = 30,
  7.         MaxSpeed = 30,
  8.     },
  9. }
  10. --TB ability fireball
  11. ProjectileBlueprint {
  12.     BlueprintId = "/projectiles/fireball03/fireball03_proj.bp",
  13.     Merge = true,
  14.     Physics = {
  15.         Acceleration = 30,
  16.         MaxSpeed = 30,
  17.     },
  18. }
  19. --TB weapon frostball
  20. ProjectileBlueprint {
  21.     BlueprintId = "/projectiles/frostball01/frostball01_proj.bp",
  22.     Merge = true,
  23.     Physics = {
  24.         Acceleration = 40,
  25.         MaxSpeed = 40,
  26.     },
  27. }

20 lines of code in place of 3 full file replacements.  Much more convenient, easier for others to see what changes you've made, and doesn't break anything.  Notice that projectiles have a completely different blueprintid structure.  You can see why in /lua/system/Blueprints.lua, where they (for whatever reason) use a different function to assign bpid.

 

In fact, Blueprints.lua contains all of the functions necessary for storing these types of blueprints, and you might notice it executes a function called 'ModBlueprints' to which it passes its all_blueprints table.  This function exists to allow mods to make sweeping, repetitive changes to these core blueprint types, and it too can be non-destructively hooked.  I don't think it's nearly as useful in Demigod as it was in FA, where you were dealing with hundreds and hundreds of unit blueprints, but for example, it can be used to do things like setting all favor items to the same cost, with about 10 lines of code - instead of manually merging 3 store blueprintids and hand-editing 30-40-some item costs:

Code: c++
  1. local prevMB = ModBlueprints
  2. ModBlueprints = function(all_bps)
  3.     local cost = 1
  4.     local stores = { 'ugbshop08', 'ugbshop08_general', 'ugbshop08_assassin', }
  5.     for _, storeid in stores do
  6.         for name, item in all_bps.Unit[storeid].Shop.Tree do
  7.             item.Cost = cost
  8.         end
  9.     end
  10.     prevMB(all_bps)
  11. end

the 'prevMB' variable is set to hold a copy of the ModBlueprints function as it exists when my mod's version of this file is hooked.  Then we re-define the ModBlueprints function, modify the blueprint entries in all_bps that we want to, and then execute the 'old' version of ModBlueprints, passing it our modified blueprint table.  Each mod that hooks this file does this in succession, starting with the latest UID - the last mod in the load order.  So they can all make their changes to the blueprint table, without destroying each others' function definition.  Pretty cool, huh?

 

If anyone didn't quite get this last bit, don't sweat it.  This was probably the quickest and and most obtuse crash course in Lua function hooking you'll encounter, but if you learn more from example than explanation, it might just make sense.

Reason for Karma (Optional)
Successfully updated karma reason!
June 2, 2010 7:25:35 PM from Demigod Forums Demigod Forums

Quoting miriyaka,
So if you needed to create a new subtable within a table, and assign a value to it via a function, this necessitates two steps:

EffectTemplates.Buff.Bramble02 = {}
EffectTemplates.Buff.Bramble02.Small = MyFunction()

Upon further thought, there are workarounds to this.  For example, an ItemBlueprint contains functioncalls (AbilityBlueprint) whose table parameters also contain functioncalls (BuffBlueprint).  This has something to do with the way the blueprint functions' metatable _call is set up, but I don't really understand exactly how that works.

Reason for Karma (Optional)
Successfully updated karma reason!
June 9, 2010 11:40:25 PM from Demigod Forums Demigod Forums

lol, only just saw this. Very nice work guys! would +1 both of you but i turned off pictures with firefox (for demigodthegame.com, which has deleted the karma and a few other buttons) and now i cant find were to reactivate them .

So does that mean even when creating new items, effects etc i should not use the format i have above or does the way you have described how to hook only apply when editing existing files?

 

NB: the reason i used my mod in the description was that files  I edited had had all previous code removed, would this still conflict with other mods?

 

Cheers, Exx

Reason for Karma (Optional)
Successfully updated karma reason!
June 10, 2010 3:22:29 AM from Demigod Forums Demigod Forums

When changing existing items only.  If you're adding a new item, then you should use a full ItemBlueprint{ ... } structure same as in the original files, just make sure your file hook only contains the blueprints you're adding, in that case, and don't just copy-paste the whole file and tag yours on the end (which I think you were clear about in the first post).

Of course, when you're adding an item and you want it to show up in the shop, you have to use a merging format to do that without breaking other mods, e.g.:

file: mymod\mod_units.bp

Code: c++
  1. UnitBlueprint {
  2.     BlueprintId = "ugbshop10",
  3.     Merge = true,
  4.     Shop = {
  5.         Tree = {
  6.             Item_My_New_Boots = {
  7.                 Cost = 500,
  8.                 ItemBP = 'Item_My_New_Boots',
  9.             },
  10.         },
  11.     },
  12. }

 

Doing the layout non-destructively is a bit trickier, because there's always the possibility that you'll use a slot that's being used by some other mod's item that otherwise wouldn't conflict with yours.  Ignoring this possibility, here's how you add an item to a shop layout without overwriting the entire table or checking whether the slot is empty:

file: mymod\hook\units\ug\b\ugbshop..\UGBShop.._ShopLayout.lua

(with the dots representing the number of the store you're adding it to - I don't know how this works with your shop tab mod)

Code: c++
  1. Layout[1][1] = 'My_Item_Blueprint_1'
  2. Layout[7][2] = 'My_Item_Blueprint_2'

The format for accessing only one key in the layout table is [row][column].  So the first example, [1][1], would place your item in the upper-left.  [7][2] would place your item in the lower-right.  As far as I'm aware, all shop tabs have a max of 7 rows (vertical) and 2 columns (horizontal).

This is really a problem with how GPG did the store layouts - there's no built-in adaptability whatsoever, and each tab has a limited number of slots, so the possibility of co-existing item mods is sadly limited.

 

I'm gonna whip up a quick little code loop that can place items adaptively, with the capability of specifying a preferred (and fallback) slot.

Reason for Karma (Optional)
Successfully updated karma reason!
June 10, 2010 6:39:36 AM from Demigod Forums Demigod Forums

The block of code at the end of this post can be pasted into any <shopname>_ShopLayout.lua file to automatically place any items you've added to that shop's blueprint via the merge method shown above, hopefully without overwriting other mods' changes.  The table format for auto-item placement is simple:

Code: c++
  1. myItems = {
  2.     [x] = 'Item_BP_Name',
  3. }

..where 'x' is both the order you want the item added in, and the number of the slot you want the item to overwrite should no free slots remain.  Items cannot be added to arbitrary points in a shop layout - it has to be contiguous or the shop tab will break, so automatic sorting is always used first, and only when the table is full is the slot number used to replace an existing item.  By 'slot', I mean the position in the layout relative to the upper-left - slot 1 would be the top-left slot, slot 5 would be in the first (left) column in the third row down, and slot 8 would be in the second (right-hand) column in the fourth row down.  You don't have to number the entries at all if you don't care what they overwrite, if they overwrite something:

Code: c++
  1. myItems = {
  2.     'Item_BP_Name_1',
  3.     'Item_BP_Name_2',
  4. }

..is also fine, and if the shop layout is full, those two items will replace the existing items in slots 1 and 2 (row 1).  The example table in the code below is for ugbshop10, and will essentially duplicate the existing 7 boot slots in the same order, filling the remaining 7 shop slots.  If I had an 8th entry in that table, it would actually overwrite the first item that it added (which got automatically added to slot 8).  It's unintuitive, as obviously you won't always necessarily know how many slots are filled by another mod, but it's the best I can do.  If anyone has a better idea of how to implement the replacement table indexing, go ahead and take a stab at it.

 

Code: c++
  1. local myItems = {
  2.     [1] = 'Item_Boot_010',
  3.     [2] = 'Item_Boot_020',
  4.     [3] = 'Item_Boot_040',
  5.     [4] = 'Item_Boot_030',
  6.     [5] = 'Item_Boot_070',
  7.     [6] = 'Item_Boot_060',
  8.     [7] = 'Item_Boot_050',
  9. }
  10. --The table keys are both the order that items will be added to empty slots
  11. --in the shop layout, and their fallback slot in the event that the layout
  12. --is full and they need to replace an existing item. Slot number is counted
  13. --downward across rows from left to right, e.g. slot 5 would be row 3, column 1;
  14. --slot 8 would be row 4, column 2, etc.  By and large, you shouldn't need to
  15. --worry about this.
  16. --Un-numbered table entries should use the lowest available key, so numbering
  17. --them is not necessary if you just want them to override slots starting at 1 anyway.
  18. --
  19. --Items must always be placed in the first available open slot, because skipping
  20. --slots causes an error and an unusable shop tab.
  21. local file = string.gsub(DiskToLocal(string.sub(debug.getinfo(1).source,2)), "^.*/([^/]+)", "%1") --get the calling filename
  22. for prefslot, itemname in myItems do
  23.     if type(itemname) == 'string' and type(prefslot) == 'number' then
  24.         --Loop through all potential rows to find the first empty slot
  25.         local placed = false
  26.         local _row = 1
  27.         while _row <= 7 and not placed do
  28.             if Layout[_row] then
  29.                 for _col = 1, 2 do
  30.                     if Layout[_row][_col] == nil or Layout[_row][_col] == '' then
  31.                         --We've found an empty column in an existing row
  32.                         Layout[_row][_col] = itemname
  33.                         placed = true
  34.                         LOG("Automatic Item Placement: "..file..": Item '"..itemname.."' successfully placed in shop layout slot ".._row..",".._col..".")
  35.                     end
  36.                 end
  37.             else
  38.                 --We've found an empty row - create a new one with our item in it
  39.                 Layout[_row] = {itemname}
  40.                 placed = true
  41.                 LOG("Automatic Item Placement: "..file..": Item '"..itemname.."' successfully placed in shop layout slot ".._row..",1.")
  42.             end
  43.             _row = _row + 1
  44.         end
  45.         if not placed then
  46.             --Fallback - if no slots were open, get our slot location, make sure it's valid
  47.             if prefslot >= 1 and prefslot <= 14 then
  48.                 local row = math.ceil(prefslot / 2)
  49.                 local col = math.floor(prefslot - (row * 2) + 2)
  50.                 local overwritten = Layout[row][col]
  51.                 Layout[row][col] = itemname
  52.                 WARN("Automatic Item Placement: "..file..": Item '"..itemname.."' placed in shop layout slot "..row..","..col..", overwriting existing item '"..overwritten.."'.")
  53.             else
  54.                 WARN("Automatic Item Placement: "..file..": Item '"..itemname.."' has a row or column reference out of bounds (max 7,2) and no free slots were available, skipping.")
  55.             end
  56.         end
  57.     else
  58.         WARN("Automatic Item Placement: "..file..": myItems["..prefslot.."] does not adhere to the format: [number] = 'Item_BP_Name', skipping.")
  59.     end
  60. end

Edit: Corrected an outdated/unhelpful error message.

Reason for Karma (Optional)
Successfully updated karma reason!
April 9, 2011 11:13:37 AM from Demigod Forums Demigod Forums

Quoting miriyaka,

Quoting miriyaka, reply 12So if you needed to create a new subtable within a table, and assign a value to it via a function, this necessitates two steps:

EffectTemplates.Buff.Bramble02 = {}
EffectTemplates.Buff.Bramble02.Small = MyFunction()
Upon further thought, there are workarounds to this.  For example, an ItemBlueprint contains functioncalls (AbilityBlueprint) whose table parameters also contain functioncalls (BuffBlueprint).  This has something to do with the way the blueprint functions' metatable _call is set up, but I don't really understand exactly how that works.

I am going to clarify this a bit for others working on more typical changes, because I stumbled a bit here recently.

If you want to insert a buffblueprint into an ability that does not already have a buff table, you'll need to do what miriyaka shows, declare a buff table before you add your buffbueprint.

Here is my example, adding an interrupt buff to spikewave. If you look at this ability, you'll notice Spikewave does not have a buff{} section.

So this is what we do, 1st line declares a buff table, than starting at the 2nd line and ending at 13 we insert a new BuffBlueprint table. (in this case an interrupt for spikewave)


Code: c#
  1. Ability.HQueenSpikeWave01.Buffs = {}
  2. table.insert(Ability.HQueenSpikeWave01.Buffs, BuffBlueprint {
  3. Name = 'SpikeInterrupt1',
  4. Debuff = true,
  5. CanBeDispelled = true,
  6. DisplayName = 'Spike Wave I',
  7. Description = 'Interrupted',
  8. BuffType = 'SPIKEINTERRUPT1',
  9. Stacks = 'REPLACE',
  10. Duration = 10,
  11. Affects = {
  12. Interrupt = {Add = 0},
  13. } })

Note: I set the duration to 10 so it is obvious to see if the ability has the interrupt added or not, so in this case the enemy will be "interrupted" for 10 seconds.

EDIT + Warning: Although the above example is correct as a way to declare a table and insert a blueprint into an ability that normally doesn't have a buff section... It has been pointed out to me that you should not add buffs to projectile weapon ability definitions. Why? Because this does not apply the buff when the projectile strikes, but rather applies the buff where you click. Opps. Pretend this is an example for WarpStrike.

Reason for Karma (Optional)
Successfully updated karma reason!
Stardock Forums v1.0.0.0    #101114  walnut1   Server Load Time: 00:00:00.0000828   Page Render Time: