Elemental: Making a Quest part 2

image

Yesterday, I wrote the general outline of how to make a quest.

Today, we’ll get into the nitty gritty:

My QuestPackage has these objects in it:

  1. The Quest Definition
  2. The Quest Location
  3. The Goodie Hut (Quest Destination)
  4. Tile Design of the Location
  5. Tile Design of the Destination (the goodie hut)

Items 4 and 5 are generated by the tile editor in the workshop, I just pasted them in.

The Quest Definition

I.                    Quest  Definition

a.       Display Name

b.      Repeatable (will it be spawned multiple times? 1 yes, 0 no)

c.       TriggerChance (% odds that it will be triggered when the trigger condition is met)

d.      Image (PNG of the image)

e.      QuestClass (Minor or Major. Completing a Major quest ends the game)

f.        TriggerType (What causes this quest to occur, we support the Location as well as other triggers such as population, turns, etc. This can be used to create random events which we’ll talk about in the future)

g.       TriggerOrigin (where does the event occur?)

h.      SpawnRating (how advances is the quest on a scale from 1 to 10)

i.         Description

j.        ShortTextAccept

k.       ShortTextDeny

l.         RewardText (dialog title for your reward)

m.    RewardImage (picture of what you’re getting)

n.      SuccessText (text that comes up when the quest is complete)

o.      GameModifier (message the game to do something)

                                                               i.      ModType  (with the corresponding Attribute options)

1.       GiveItem (attribute is name of item)

2.       Unit (StrVal provides the key and if necessary <Value> provides the amount)

a.       TransferUnitStat

b.      StealUnitStat

c.       Lifesteal

d.      IncreaseDecreaseUnitStats

e.      AdjustUnitStat

f.        AdjustCasterUnitStat

g.       UnlockUnitAbility

h.      CharmTarget

i.         CurHealth

j.        CurMana

k.       ManaBurn

l.         GiveExperience

m.    UnitJoinArmy

n.      TargetMovesBack

o.      SummonUnit (UnitClass provides class name, StrVal provides title)

3.       Player

a.       Treasury

b.      SpellPointBonus

c.       AbilityBonus

d.      EssenceBonusForAllChampions

e.      UnlockResource

f.        UnlockImprovement

g.       UnlockTech

h.      UnlockUnitAction

i.         UnlockCityAction

j.        UnlockSpellbook

k.       UnlockDiplomacyWndAbility

l.         RaiseResourceCap

m.    UpgradeCityWalls

n.      ExposeCity

o.      ExposeSovereign

p.      ExposeGoodieHut

q.      SpawnQuestLocations

r.        UpdateSpawnRating

                                                                                                                                       i.      NPC

                                                                                                                                     ii.      CREATURE

                                                                                                                                    iii.      QUEST

                                                                                                                                   iv.      GOODIEHUT

                                                                                                                                     v.      CRYSTAL

                                                                                                                                   vi.      FOOD

                                                                                                                                  vii.      METAL

                                                                                                                                viii.      SHARD

                                                                                                                                   ix.      GOLD

s.       UpdateRecruitables

                                                                                                                                       i.      SPIDERS

                                                                                                                                     ii.      SHRILLS

                                                                                                                                    iii.      DRATH

                                                                                                                                   iv.      DARKLINGS

                                                                                                                                     v.      UMBERDROTHS

                                                                                                                                   vi.      DROTA

                                                                                                                                  vii.      DRAGONS

                                                                                                                                viii.      DEMONS

t.        MarriageProposal

4.       Map

a.  TransportUnit

b.  PlaceEnvironment

c.  PlaceTerrain

d.  GreaterRaiseLand

e.  RaiseLand

f.  LowerLand

g.  SetEnvironment

h.      ReviveLand

i.         DestroyObjects

j.        RandomTerraform

k.       SummonUnit

l.         CreateGoodieHut

m.    CreateResourceHoard

n.      CreateWorldProp

o.      Reveal

p.      BlockTile

5.       City

a.       SummonRandomGuardian

b.      PlaceEnvironmentOnCity

p.      QuestObjectiveDef

                                                               i.      ObjectiveID (defining its numerical ID)

                                                             ii.      NextObjectiveID (telling it where to go next or -1 if this ends the quest)

                                                            iii.      Description

                                                           iv.      GameModifier

                                                             v.      QuestConditionDef (what determines whether this quest is complete?)

1.       Class (Success of Failure)

2.       Type (what type of condition is it)

a.       ClearGoodieHut (TextData then used to say which goodiehut)

b.      KillMonster (TextData used to identify which monster)

c.       UnitKilled

d.      BuildImprovement

e.      ReturnItemToCapital

f.        ReturnItemToQuestLocation

g.       CheckForItem

h.      ReturnItemToOriginCity

i.         UnitLevelUp

j.        UnitEntersOriginCity

3.       Faction (if applicable)

4.       Flag (AllConditionsMet forces all conditions to be met for it to be a successful quest, RevealTarget is useful for showing the player where the target is)

QuestLocationType

This is used to define the tile that will be used as the quest location. You can (and probably should) use existing quest location types.

GoodieHutType

This is used if you want your quest to spawn a goodie hut to send the player to. You could always just do the “bring me 5 wolf pelts back to me and I’ll give you gold” type quest instead and make sure you are spawning wolves that have the item WolfPelt as their treasure.

Download

To download the full quest here it is:

http://dl.dropbox.com/u/8051911/Quest_UrgentMessage.zip

You will need to unzip the XML file into your elemental\data\english\core quests directory

(in the future, I have put in the request that a packaged quest can just go into the user’s quest directory)

Other Notes

The hardest thing about making quests right now is the difficulty in testing them. One typo in a quest can make it just not work. This is why I’m lobbying internally (or externally) to get a quest editor made that eases this but will require more documentation. As you can see from above, the quest system is powerful but we’ve only scratched the surface so far.

138,427 views 47 replies
Reply #1 Top

<3

This is what I'm talkin' about.  Excellent information!  Thank you!

(in the future, I have put in the request that a packaged quest can just go into the user’s quest directory)

I hope you're able to get that implemented.  Would it be a compressed file that gets stored or would it be a sub folder that gets copied?  I'm hoping for the latter.  With .NET you can set an option to scan all subfolders vs. the top directory.  I don't know what it takes in C/C++.

Reply #2 Top

That is tremendously helpful!

Reply #3 Top

I don't think StealUnitStat or IncreaseDecreaseUnitStat works

Great reference post. Need more of these!

Reply #4 Top

Quoting Gravedancer, reply 1


This is what I'm talkin' about.  Excellent information!  Thank you!


(in the future, I have put in the request that a packaged quest can just go into the user’s quest directory)

I hope you're able to get that implemented.  Would it be a compressed file that gets stored or would it be a sub folder that gets copied?  I'm hoping for the latter.  With .NET you can set an option to scan all subfolders vs. the top directory.  I don't know what it takes in C/C++.

Normally you would end up with several different files (the quest def, the goodie hut, the quest location, and the various tiles).  I took them all and put them into a single XML file that you can drop into a folder.  But I have to drop them into a particular folder in the game directory rather than the user directory which is what I'd prefer.

 

Reply #5 Top

Code: c++
  1. <div>
  2. else if( _tcsicmp( szStat, _T("TriggerType") ) == 0 )
  3. {
  4.     if( _tcsicmp( szValue, _T("GoodieHut") ) == 0 )
  5.         this-&gt;SetTriggerType( QUEST_TRIGGER_GOODIE_HUT );
  6.     else if( _tcsicmp( szValue, _T("CityPopulation") ) == 0 )
  7.         this-&gt;SetTriggerType( QUEST_TRIGGER_CITY_POPULATION );
  8.     else if( _tcsicmp( szValue, _T("TotalPopulation") ) == 0 )
  9.         this-&gt;SetTriggerType( QUEST_TRIGGER_TOTAL_POPULATION );
  10.     else if( _tcsicmp( szValue, _T("UnitLevel") ) == 0 )
  11.         this-&gt;SetTriggerType( QUEST_TRIGGER_UNIT_LEVEL );
  12.     else if( _tcsicmp( szValue, _T("ImprovementBuilt") ) == 0 )
  13.         this-&gt;SetTriggerType( QUEST_TRIGGER_IMPROVEMENT_BUILT );
  14.     else if( _tcsicmp( szValue, _T("TechnologyResearched") ) == 0 )
  15.         this-&gt;SetTriggerType( QUEST_TRIGGER_TECHNOLOGY_RESEARCHED );
  16.     else if( _tcsicmp( szValue, _T("SpellResearched") ) == 0 )
  17.         this-&gt;SetTriggerType( QUEST_TRIGGER_SPELL_RESEARCHED );
  18.     else if( _tcsicmp( szValue, _T("QuestLocation") ) == 0 )
  19.         this-&gt;SetTriggerType( QUEST_TRIGGER_QUEST_LOCATION )
  20. }
  21. </div>
  22.  

Reply #6 Top

Could we please eventually get a corresponding Lock GameModifier to all these unlocks up there? Sometimes it could be useful to take away, or temporary hide particular things (spellbooks etc.), especially when writing trial-type quests or negative events.

Reply #8 Top

A few questions, in no particular (logical) order:

1. How do I structure GameModifiers to add 2 rewards.  Say GiveItem and Unit:GiveExperience.  Is that just 2 GameModifier elements? 

2. What is the correct enumerated values for TriggerType?  TriggerOrigin?

3. What is the Token_BestFriend?  Where is that defined?  Can I define my own item under the QuestDef element?

4. How do I define a monster/unit that needs to be killed?

5. How can I put a time/turn limit between multiple QuestObjectiveDef elements?

6. When sending player to multiple locations, each location is defined as by GoodieHutType.  Only the QuestLocationType defines the where teh quest starts?

7. Explain PrefQuestLoc. 

8. How can I give quest without player coming to a location?

9. Do you have an XSD?

10. Where do the images need to be at?  Filesystem wise.

I imagine I will have more as I work through a few quests that I have in mind.

Reply #9 Top

11. Can we tie the Accept/Deny to QuestObjectiveDef elements? Will you help me, IF Answer=Yes, Yippee spawn GoodieHut, ELSE Spawn mad quest giver?

 

Reply #10 Top

I think it would be more helpful if the quest editor was implemented in-game (and interconnected with the rest of the editors).

 

Reply #11 Top

Excellent information. :) Thanks.

Reply #12 Top

Great Brad,

why your here and slightly unrelated can you list the damage types for units, I've been modding weapons also could I use the unit lifesteal there and how exactly does it work.

Reply #13 Top



Yesterday, I wrote the general outline of how to make a quest.

......


1.       Class (Success of Failure)
2.       Type (what type of condition is it)
a.       ClearGoodieHut (TextData then used to say which goodiehut)
b.      KillMonster (TextData used to identify which monster)
c.       UnitKilled
d.      BuildImprovement
e.      ReturnItemToCapital
f.        ReturnItemToQuestLocation
g.       CheckForItem
h.      ReturnItemToOriginCity
i.         UnitLevelUp
j.        UnitEntersOriginCity

......


 As for the quest in 1.07 (for killing rats), I get my champion there and trigger the quest, then my champion to the location of the quest and did the quest (killed the rats), but i do not get a reward when my champion return to the hut. I tried a few times until i get my sovereign to take the rat corpse in her inventory and go to the hut to receive the reward.

does g. CheckForItem only check the sovereign inventory and location to trigger?

Reply #14 Top

Is there a way to spawn an encounter that is relatively equal to the party's strength that started the quest?

 

  I notice the levelhi levello tags in some of the other core quests, but they don't seem to be used (or I don't understand what they are for)

Reply #15 Top

Hmmm... am I the only one thinking we're going to need some kind mod manager in order to keep track of which one we want to activate? Just shoving it into a Mod folder and a Quest folder isn't going to cut it. Remembering which xml file does which isn't exactly the most ideal solution when the number of mods start growing.

Reply #16 Top

Quoting DarklingGnostic, reply 13

As for the quest in 1.07 (for killing rats), I get my champion there and trigger the quest, then my champion to the location of the quest and did the quest (killed the rats), but i do not get a reward when my champion return to the hut. I tried a few times until i get my sovereign to take the rat corpse in her inventory and go to the hut to receive the reward.

Try transferring the dead rat to whoever triggered the quest, then re-enter the inn.

Reply #17 Top

Quoting Kalin, reply 15
Hmmm... am I the only one thinking we're going to need some kind mod manager in order to keep track of which one we want to activate? Just shoving it into a Mod folder and a Quest folder isn't going to cut it. Remembering which xml file does which isn't exactly the most ideal solution when the number of mods start growing.

If mods could stored in their own folder, it would be easier to manage.

Otherwise the challenge is for a mod manager application being able to identify which files are associated with each other.  You could parse a quest file and find all the dependencies (outside of the core game folders) and remove them.  However what if you built a quest using tiles that you downloaded for another quest, mod, campaign, etc.  Unless when you build a quest, all the elements of that quest get copied and identified as related/dependent on that quest.

Another option would be to initially store the mod files outside of the user folders in separate folders for each mod.  Then use the mod manager to select which mods to "install" (basically copy to the user folders) and record what was installed and where.  If you wanted to uninstall a mod, then it could look up what files were installed and remove them.

A major problem with having a mod manager outside of the game is that if you load a saved game, I don't think the game currently checks to make sure all the mods it's dependent on are loaded.  So if someone built a mod manager and people didn't understand how it worked, I think there would be a risk that they'd inadvertently remove mod files, break their save game, and then get mad at both Stardock and the person who wrote the mod manager.

Reply #18 Top

CAUTION: CODE AHEAD.

Let me see if I can get this .NET C# code snippet to work.

To Create the existing Quest using XmlSerialization

Base Serialization Class Structure based on Quest provided.

Code: c#
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Xml.Serialization;
  4. namespace Elemental.QuestWizard
  5. {
  6.     public class BaseElement
  7.     {
  8.         [XmlAttribute]
  9.         public string InternalName { get; set; }
  10.         public BaseElement()
  11.             : this(string.Empty)
  12.         {
  13.         }
  14.         public BaseElement(string internalName)
  15.         {
  16.             this.InternalName = internalName;
  17.         }
  18.     }
  19.     public class QuestPackage
  20.     {
  21.         [XmlElement]
  22.         public QuestDef QuestDef { get; set; }
  23.         [XmlElement]
  24.         public QuestLocationType QuestLocationType { get; set; }
  25.         [XmlElement(ElementName = "GoodieHutType", Type = typeof(GoodieHutType))]
  26.         public List&lt;GoodieHutType&gt; GoodieHutType { get; set; }
  27.         [XmlElement(ElementName = "TileDesign", Type = typeof(TileDesign))]
  28.         public List&lt;TileDesign&gt; TileDesign { get; set; }
  29.         public QuestPackage()
  30.         {
  31.             this.QuestDef = new QuestDef();
  32.             this.QuestLocationType = new QuestLocationType();
  33.             this.GoodieHutType = new List&lt;GoodieHutType&gt;();
  34.             this.TileDesign = new List&lt;TileDesign&gt;();
  35.         }
  36.         public GoodieHutType AddGoodieHut(string internalName)
  37.         {
  38.             GoodieHutType item = new GoodieHutType(internalName);
  39.             this.GoodieHutType.Add(item);
  40.             return item;
  41.         }
  42.         public TileDesign AddTileDesign(string internalName, string innerXml)
  43.         {
  44.             TileDesign item = new TileDesign(internalName, innerXml);
  45.             this.TileDesign.Add(item);
  46.             return item;
  47.         }
  48.     }
  49.     public class QuestDef : BaseElement
  50.     {
  51.         [XmlElement]
  52.         public string DisplayName { get; set; }
  53.         [XmlElement]
  54.         public string PrefQuestLoc { get; set; }
  55.         [XmlElement]
  56.         public int Repeatable { get; set; }
  57.         [XmlElement]
  58.         public int TriggerChance { get; set; }
  59.         [XmlElement]
  60.         public string Image { get; set; }
  61.         [XmlElement]
  62.         public string QuestClass { get; set; }
  63.         [XmlElement]
  64.         public string TriggerType { get; set; }
  65.         [XmlElement]
  66.         public string TriggerOrigin { get; set; }
  67.         [XmlElement]
  68.         public int SpawnRating { get; set; }
  69.         [XmlElement]
  70.         public string Description { get; set; }
  71.         [XmlElement]
  72.         public string ShortTextAccept { get; set; }
  73.         [XmlElement]
  74.         public string ShortTextDeny { get; set; }
  75.         [XmlElement]
  76.         public string RewardText { get; set; }
  77.         [XmlElement]
  78.         public string RewardImage { get; set; }
  79.         [XmlElement]
  80.         public string SuccessText { get; set; }
  81.         [XmlElement(ElementName = "GameModifier", Type = typeof(GameModifier))]
  82.         public List&lt;GameModifier&gt; GameModifier { get; set; }
  83.         [XmlElement(ElementName = "QuestObjectiveDef", Type = typeof(QuestObjectiveDef))]
  84.         public List&lt;QuestObjectiveDef&gt; QuestObjectiveDef { get; set; }
  85.         public QuestDef() : this(string.Empty) { }
  86.         public QuestDef(string internalName)
  87.             : base(internalName)
  88.         {
  89.             this.GameModifier = new List&lt;GameModifier&gt;();
  90.             this.QuestObjectiveDef = new List&lt;QuestObjectiveDef&gt;();
  91.         }
  92.         public GameModifier AddGameModifier(string internalName)
  93.         {
  94.             GameModifier item = new GameModifier(internalName);
  95.             this.GameModifier.Add(item);
  96.             return item;
  97.         }
  98.         public QuestObjectiveDef AddQuestObjectiveDef(string internalName)
  99.         {
  100.             QuestObjectiveDef item = new QuestObjectiveDef(internalName);
  101.             this.QuestObjectiveDef.Add(item);
  102.             return item;
  103.         }
  104.     }
  105.     public class GoodieHutType : BaseElement
  106.     {
  107.         [XmlElement]
  108.         public string Name { get; set; }
  109.         [XmlElement]
  110.         public string Description { get; set; }
  111.         [XmlElement]
  112.         public Medallions Medallions { get; set; }
  113.         [XmlElement]
  114.         public int Destructable { get; set; }
  115.         [XmlElement]
  116.         public string TileDesign { get; set; }
  117.         [XmlElement]
  118.         public string DrawnIcon { get; set; }
  119.         [XmlElement]
  120.         public int IconSize { get; set; }
  121.         public GoodieHutType() : this(string.Empty) { }
  122.         public GoodieHutType(string internalName)
  123.         {
  124.             this.InternalName = internalName;
  125.             this.Medallions = new Medallions();
  126.         }
  127.     }
  128.     public class QuestLocationType : GoodieHutType
  129.     {
  130.         [XmlElement]
  131.         public int Rarity { get; set; }
  132.         public QuestLocationType()
  133.             : base(string.Empty)
  134.         {
  135.         }
  136.         public QuestLocationType(string internalName)
  137.             : base(internalName)
  138.         {
  139.         }
  140.     }
  141.     public class TileDesign : BaseElement, IXmlSerializable
  142.     {
  143.         public string InnerXml { get; set; }
  144.         public TileDesign() { }
  145.         public TileDesign(string internalName, string innerXML)
  146.         {
  147.             this.InternalName = internalName;
  148.             this.InnerXml = innerXML;
  149.         }
  150.         #region IXmlSerializable Members
  151.         public System.Xml.Schema.XmlSchema GetSchema()
  152.         {
  153.             return null;
  154.         }
  155.         public void ReadXml(System.Xml.XmlReader reader)
  156.         {
  157.             this.InnerXml = reader.ReadInnerXml();
  158.         }
  159.         public void WriteXml(System.Xml.XmlWriter writer)
  160.         {
  161.             writer.WriteAttributeString("InternalName", this.InternalName);
  162.             writer.WriteRaw(this.InnerXml);
  163.         }
  164.         #endregion
  165.     }
  166.     public class GameModifier : BaseElement
  167.     {
  168.         [XmlElement]
  169.         public string ModType { get; set; }
  170.         [XmlElement]
  171.         public string Attribute { get; set; }
  172.         [XmlElement]
  173.         public string UnitClass { get; set; }
  174.         [XmlElement]
  175.         public string StrVal { get; set; }
  176.         [XmlElement]
  177.         public int Radius { get; set; }
  178.         public GameModifier() : this(string.Empty) { }
  179.         public GameModifier(string internalName)
  180.         {
  181.             this.InternalName = internalName;
  182.         }
  183.     }
  184.     public class QuestObjectiveDef : BaseElement
  185.     {
  186.         [XmlElement]
  187.         public int ObjectiveID { get; set; }
  188.         [XmlElement]
  189.         public int NextObjectiveID { get; set; }
  190.         [XmlElement]
  191.         public string Description { get; set; }
  192.         [XmlElement(ElementName = "GameModifier", Type = typeof(GameModifier))]
  193.         public List&lt;GameModifier&gt; GameModifier { get; set; }
  194.         [XmlElement]
  195.         public QuestConditionDef QuestConditionDef { get; set; }
  196.         public QuestObjectiveDef() : this(string.Empty) { }
  197.         public QuestObjectiveDef(string internalName)
  198.         {
  199.             this.InternalName = internalName;
  200.             this.GameModifier = new List&lt;GameModifier&gt;();
  201.             this.QuestConditionDef = new QuestConditionDef();
  202.         }
  203.         public GameModifier AddGameModifier(string internalName)
  204.         {
  205.             GameModifier item = new GameModifier(internalName);
  206.             this.GameModifier.Add(item);
  207.             return item;
  208.         }
  209.     }
  210.     public class QuestConditionDef : BaseElement
  211.     {
  212.         [XmlElement]
  213.         public string Description { get; set; }
  214.         [XmlElement]
  215.         public string Class { get; set; }
  216.         [XmlElement]
  217.         public string Type { get; set; }
  218.         [XmlElement]
  219.         public string TextData { get; set; }
  220.         [XmlElement]
  221.         public string Flag { get; set; }
  222.         public QuestConditionDef() : this(string.Empty) { }
  223.         public QuestConditionDef(string internalName)
  224.         {
  225.             this.InternalName = internalName;
  226.         }
  227.     }
  228.     public class Medallions
  229.     {
  230.         public string All { get; set; }
  231.         public Medallions() : this(string.Empty) { }
  232.         public Medallions(string all) { this.All = all; }
  233.     }
  234. }

Now, to read in existing

Code: c#
  1.         public void ReadQuest()
  2.         {
  3.             XmlSerializerFactory factory = new XmlSerializerFactory();
  4.             XmlSerializer serializer = factory.CreateSerializer(typeof(QuestPackage));
  5.             using (System.IO.StreamReader reader = new System.IO.StreamReader(@"C:\Temp\Q\Quest_UrgentMessage\Quest_UrgentMessage.xml"))
  6.             {
  7.                 QuestPackage package = serializer.Deserialize(reader) as QuestPackage;
  8.             }
  9.         }

Or just to recreate sample using c#

Code: c#
  1. public void WriteQuest()
  2.         {
  3.             XmlSerializerFactory factory = new XmlSerializerFactory();
  4.             XmlSerializer serializer = factory.CreateSerializer(typeof(QuestPackage));
  5.             QuestPackage package = new QuestPackage();
  6.             #region TileDesign
  7.            
  8.             TileDesign questStart = package.AddTileDesign("Q_UrgentMessage_L", @"&lt;DisplayName&gt;Q_UrgentMessage_L&lt;/DisplayName&gt;
  9.     &lt;Thumbnail&gt;\Game\Gfx\tile_icon.png&lt;/Thumbnail&gt;
  10.     &lt;DecalName&gt;Gfx\Decals\Improvement_Ground_01.png&lt;/DecalName&gt;
  11.     &lt;LowResShadow&gt;Q_UrgentMessage_L_shadow_256.png&lt;/LowResShadow&gt;
  12.     &lt;HighResShadow&gt;Q_UrgentMessage_L_shadow_512.png&lt;/HighResShadow&gt;
  13.     &lt;ShadowTransparency&gt;70&lt;/ShadowTransparency&gt;
  14.     &lt;IsCore&gt;0&lt;/IsCore&gt;
  15.     &lt;IsHiddenFromEditor&gt;0&lt;/IsHiddenFromEditor&gt;
  16.     &lt;SubTileFlags&gt;15&lt;/SubTileFlags&gt;
  17.     &lt;TileDesignObject InternalName=""_obj0""&gt;
  18.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  19.       &lt;ObjectDefName&gt;K_Study_01&lt;/ObjectDefName&gt;
  20.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  21.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  22.       &lt;Position&gt;21.774414,0.000000,-21.146973&lt;/Position&gt;
  23.       &lt;Rotation&gt;0.000000,2.356194,0.000000&lt;/Rotation&gt;
  24.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  25.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  26.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  27.     &lt;/TileDesignObject&gt;
  28.     &lt;TileDesignObject InternalName=""_obj1""&gt;
  29.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  30.       &lt;ObjectDefName&gt;TownTree_Mid&lt;/ObjectDefName&gt;
  31.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  32.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  33.       &lt;Position&gt;28.007462,0.000000,27.489655&lt;/Position&gt;
  34.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  35.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  36.       &lt;Scale&gt;0.7200&lt;/Scale&gt;
  37.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  38.     &lt;/TileDesignObject&gt;
  39.     &lt;TileDesignObject InternalName=""_obj2""&gt;
  40.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  41.       &lt;ObjectDefName&gt;AppleTree_3&lt;/ObjectDefName&gt;
  42.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  43.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  44.       &lt;Position&gt;-25.890381,0.000000,-22.748825&lt;/Position&gt;
  45.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  46.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  47.       &lt;Scale&gt;1.8000&lt;/Scale&gt;
  48.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  49.     &lt;/TileDesignObject&gt;
  50.     &lt;TileDesignPath InternalName=""_path0""/&gt;");
  51.             TileDesign questEnd = package.AddTileDesign("Q_UrgentMessage_D", @"&lt;DisplayName&gt;Q_UrgentMessage_D&lt;/DisplayName&gt;
  52.     &lt;Thumbnail&gt;\Game\Gfx\tile_icon.png&lt;/Thumbnail&gt;
  53.     &lt;DecalName&gt;Gfx\Decals\DirtQuarterPath.png&lt;/DecalName&gt;
  54.     &lt;LowResShadow&gt;Q_UrgentMessage_D_shadow_256.png&lt;/LowResShadow&gt;
  55.     &lt;HighResShadow&gt;Q_UrgentMessage_D_shadow_512.png&lt;/HighResShadow&gt;
  56.     &lt;ShadowTransparency&gt;70&lt;/ShadowTransparency&gt;
  57.     &lt;IsCore&gt;0&lt;/IsCore&gt;
  58.     &lt;IsHiddenFromEditor&gt;0&lt;/IsHiddenFromEditor&gt;
  59.     &lt;SubTileFlags&gt;15&lt;/SubTileFlags&gt;
  60.     &lt;TileDesignObject InternalName=""_obj0""&gt;
  61.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  62.       &lt;ObjectDefName&gt;K_Hovel_01&lt;/ObjectDefName&gt;
  63.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  64.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  65.       &lt;Position&gt;-13.748528,0.000000,-21.005386&lt;/Position&gt;
  66.       &lt;Rotation&gt;0.000000,4.188790,0.000000&lt;/Rotation&gt;
  67.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  68.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  69.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  70.     &lt;/TileDesignObject&gt;
  71.     &lt;TileDesignObject InternalName=""_obj1""&gt;
  72.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  73.       &lt;ObjectDefName&gt;K_Silo_Basic_02&lt;/ObjectDefName&gt;
  74.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  75.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  76.       &lt;Position&gt;29.099831,0.000000,19.113419&lt;/Position&gt;
  77.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  78.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  79.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  80.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  81.     &lt;/TileDesignObject&gt;
  82.     &lt;TileDesignObject InternalName=""_obj2""&gt;
  83.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  84.       &lt;ObjectDefName&gt;K_Rope_Fence_01&lt;/ObjectDefName&gt;
  85.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  86.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  87.       &lt;Position&gt;17.473774,0.000000,15.799500&lt;/Position&gt;
  88.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  89.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  90.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  91.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  92.     &lt;/TileDesignObject&gt;
  93.     &lt;TileDesignObject InternalName=""_obj3""&gt;
  94.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  95.       &lt;ObjectDefName&gt;K_Rope_Fence_01&lt;/ObjectDefName&gt;
  96.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  97.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  98.       &lt;Position&gt;3.150505,1.000000,7.657745&lt;/Position&gt;
  99.       &lt;Rotation&gt;0.000000,5.585054,0.000000&lt;/Rotation&gt;
  100.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  101.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  102.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  103.     &lt;/TileDesignObject&gt;
  104.     &lt;TileDesignObject InternalName=""_obj4""&gt;
  105.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  106.       &lt;ObjectDefName&gt;AnnieDog&lt;/ObjectDefName&gt;
  107.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  108.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  109.       &lt;Position&gt;25.500847,0.000000,-0.212799&lt;/Position&gt;
  110.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  111.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  112.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  113.       &lt;BaseScale&gt;0.1600&lt;/BaseScale&gt;
  114.     &lt;/TileDesignObject&gt;
  115.     &lt;TileDesignObject InternalName=""_obj5""&gt;
  116.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  117.       &lt;ObjectDefName&gt;AnnieDog&lt;/ObjectDefName&gt;
  118.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  119.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  120.       &lt;Position&gt;11.278976,0.000000,-9.913300&lt;/Position&gt;
  121.       &lt;Rotation&gt;0.000000,4.974189,0.000000&lt;/Rotation&gt;
  122.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  123.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  124.       &lt;BaseScale&gt;0.1600&lt;/BaseScale&gt;
  125.     &lt;/TileDesignObject&gt;
  126.     &lt;TileDesignObject InternalName=""_obj6""&gt;
  127.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  128.       &lt;ObjectDefName&gt;AnnieDog&lt;/ObjectDefName&gt;
  129.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  130.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  131.       &lt;Position&gt;33.214260,0.000000,-33.910858&lt;/Position&gt;
  132.       &lt;Rotation&gt;0.000000,2.443461,0.000000&lt;/Rotation&gt;
  133.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  134.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  135.       &lt;BaseScale&gt;0.1600&lt;/BaseScale&gt;
  136.     &lt;/TileDesignObject&gt;
  137.     &lt;TileDesignObject InternalName=""_obj7""&gt;
  138.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  139.       &lt;ObjectDefName&gt;PineTreePatch2&lt;/ObjectDefName&gt;
  140.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  141.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  142.       &lt;Position&gt;-23.940048,0.000000,25.861465&lt;/Position&gt;
  143.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  144.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  145.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  146.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  147.     &lt;/TileDesignObject&gt;
  148.     &lt;TileDesignObject InternalName=""_obj8""&gt;
  149.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  150.       &lt;ObjectDefName&gt;PineTreePatch2&lt;/ObjectDefName&gt;
  151.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  152.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  153.       &lt;Position&gt;0.854750,0.000000,29.898254&lt;/Position&gt;
  154.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  155.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  156.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  157.       &lt;BaseScale&gt;1.0000&lt;/BaseScale&gt;
  158.     &lt;/TileDesignObject&gt;
  159.     &lt;TileDesignObject InternalName=""_obj9""&gt;
  160.       &lt;ObjectType&gt;PropModelType&lt;/ObjectType&gt;
  161.       &lt;ObjectDefName&gt;FallenMaleTalking1&lt;/ObjectDefName&gt;
  162.       &lt;ObjectLOD&gt;4&lt;/ObjectLOD&gt;
  163.       &lt;IgnoreTerrain&gt;0&lt;/IgnoreTerrain&gt;
  164.       &lt;Position&gt;1.167755,0.000000,-12.808250&lt;/Position&gt;
  165.       &lt;Rotation&gt;0.000000,0.000000,0.000000&lt;/Rotation&gt;
  166.       &lt;RotationSpeed&gt;0.000000,0.000000,0.000000&lt;/RotationSpeed&gt;
  167.       &lt;Scale&gt;1.0000&lt;/Scale&gt;
  168.       &lt;BaseScale&gt;0.1350&lt;/BaseScale&gt;
  169.     &lt;/TileDesignObject&gt;
  170.     &lt;TileDesignPath InternalName=""_path0""/&gt;");
  171.             #endregion
  172.             GoodieHutType hut = package.AddGoodieHut("Q_UrgentMessage_D");
  173.             hut.Name = "Orlof's";
  174.             hut.Description = "The sound of dogs barking can be heard for miles.";
  175.             hut.Medallions.All = "M_Wolf_Dog_Card_01.png";
  176.             hut.Destructable = 1;
  177.             hut.TileDesign = questEnd.InternalName;
  178.             hut.DrawnIcon = "Gfx/TacticalIcons/K_Slum1.png";
  179.             hut.IconSize = 75;
  180.             package.QuestLocationType.InternalName = "Q_UrgentMessage_L";
  181.             package.QuestLocationType.Name = "Dennis's Estate";
  182.             package.QuestLocationType.Description = "This estate is something we may want to check out.";
  183.             package.QuestLocationType.Rarity = 70;
  184.             package.QuestLocationType.Medallions.All = "Building_Inn1.png";
  185.             package.QuestLocationType.TileDesign = "Q_UrgentMessage_L";
  186.             package.QuestLocationType.DrawnIcon = "Gfx/TacticalIcons/K_Inn1.png";
  187.             package.QuestLocationType.Destructable = 0;
  188.             package.QuestLocationType.IconSize = 75;
  189.             package.QuestDef.InternalName = "Quest_UrgentMessage";
  190.             package.QuestDef.DisplayName = "An urgent message";
  191.             package.QuestDef.PrefQuestLoc = package.QuestLocationType.InternalName;
  192.             package.QuestDef.Repeatable = 0;
  193.             package.QuestDef.TriggerChance = 100;
  194.             package.QuestDef.Image = "Academy_Medallion.png";
  195.             package.QuestDef.QuestClass = "Minor";
  196.             package.QuestDef.TriggerType = "QuestLocation";
  197.             package.QuestDef.TriggerOrigin = "EventLocation";
  198.             package.QuestDef.SpawnRating = 1;
  199.             package.QuestDef.Description = "You have come upon the homestead of Dennis Lacros. Upon seeing you, you exclaims \"My Lord! What an honor, please join me for dinner.\" Over Dinner, he explains how he and some others in the area have been working together to rid the area of wolves. He works with a local breeder of hounds named Olof who has been providing him with hounds to drive off the wolves.  He asks if you would be willing to help him by delivering an urgent message that he needs another dozen hounds as soon as possible. What do you say?";
  200.             package.QuestDef.ShortTextAccept = "I shall deliver the message.";
  201.             package.QuestDef.ShortTextDeny = "I'm too busy for this.";
  202.             package.QuestDef.RewardText = "Token of Man's Best Friend";
  203.             package.QuestDef.RewardImage = "M_Wolf_Dog_Card_01.png";
  204.             package.QuestDef.SuccessText = "Master Orlof greets you as you arrive. \"Ah, Dennis needs more of me hounds! Barlabell just had a litter last week, they will make mighty companions. In the meantime, I have something for you.\"";
  205.             GameModifier reward = package.QuestDef.AddGameModifier("Reward");
  206.             reward.ModType = "GiveItem";
  207.             reward.Attribute = "Token_BestFriend";
  208.             QuestObjectiveDef objective = package.QuestDef.AddQuestObjectiveDef("Bring Message");
  209.             objective.ObjectiveID = 0;
  210.             objective.NextObjectiveID = -1;
  211.             objective.Description = "Master Orlof's kennel is not far from here.";
  212.             GameModifier createGoodieHut = objective.AddGameModifier("CreateGoodieHut");
  213.             createGoodieHut.ModType = "Map";
  214.             createGoodieHut.Attribute = "CreateGoodieHut";
  215.             createGoodieHut.UnitClass = questEnd.InternalName;
  216.             createGoodieHut.StrVal = "Master Orlof's";
  217.             createGoodieHut.Radius = 10;
  218.             objective.QuestConditionDef.InternalName = "Arrive at Orlof's";
  219.             objective.QuestConditionDef.Description = "Arrive with message.";
  220.             objective.QuestConditionDef.Class = "Success";
  221.             objective.QuestConditionDef.Type = "ClearGoodieHut";
  222.             objective.QuestConditionDef.TextData = questEnd.InternalName;
  223.             objective.QuestConditionDef.Flag = "RevealTarget";
  224.             using (System.IO.StreamWriter writer = new System.IO.StreamWriter(@"C:\\Temp\Temp.xml"))
  225.             {
  226.                 serializer.Serialize(writer, package);
  227.             }
  228.         }

Reply #20 Top

Quoting Anthony_Christianson, reply 19
Now, on to creating the UI for the wizard.

may i be the first to say... i love you man... no, i mean it... i love you!

:')  

oh, and k1   for you my good man...

Reply #21 Top

@Frogboy


Me, Kenata, and Gnilbert have been noticing some... irregularities in the [calculate] function, most noticeably when you attempt to decrease an enemy stat using AdjustUnitStat after dealing damage, or just generally applying negative modifiers to statistics.  If you want to look into this, I can PM you some examples, but I think it might be most productive if you had someone on the team actually write some script to do this and take note of the results.

Reply #22 Top

I haven't voted for which one you should focus on first because I personally don't think you can focus on just one at a time. Not if you want a great result anyway.

I think what is needed is a global design which fits all the major gameplay elements of the game together into a framework were all key strategies are viable and balanced against each other. The design then needs to drill down lower and lower into each level of the game making sure that each level is both balanced and has interesting choices / noticeable trade offs. Finally the lowest level data needs to be refined so that items/spells/units are balanced and interesting.

 

Examples off the top of my head - you might disagree with some of the examples but hopefully not the theory:

Top Level : Arcane vs Tech, Champions vs Regular Units,  Conquest vs Spell vs Quest victory conditions, aggression vs building / turtling gamestyles.

Mid Level : Warfare vs Adventure vs Civilisation techs, few large cities (eg level 5) vs more small cities (eg level 2), imbuing many spellcaster champions vs building up a few very powerful spellcasters, ranged vs melee vs spellcasters (both for champions and regular units).

Low Level : Fireball vs Frost Blast, Sword vs Axe, Combat Attack vs Defence vs Combat Speed, Strength vs Dexterity vs Charisma etc.

In all cases the various options the player is choosing between need to be moderately balanced (ie several choices are viable) but also interestingly different (ie it makes a difference which one you pick), otherwise there is either only one choice or the choice doesn't matter (both of which are equally bad).

 

IMO tinkering with one part in isolation will probably just lead to slightly more polish in that area while leaving the overal balance of the game in disarray.

 

Reply #23 Top

The hardest thing about making quests right now is the difficulty in testing them. One typo in a quest can make it just not work. This is why I’m lobbying internally (or externally) to get a quest editor made that eases this but will require more documentation. As you can see from above, the quest system is powerful but we’ve only scratched the surface so far.

Are you able to release an XSD file so we at least can validate the structure, if not the actual values? Relative work should be significantly less for a pretty sizable initial benefit.

Reply #24 Top

Quoting Anthony_Christianson, reply 19
Now, on to creating the UI for the wizard.

Wow. :')

I hate to ask but I'm going to anyways... how long did it take you to come up with that code?

Reply #25 Top

Would it be a compressed file that gets stored or would it be a sub folder that gets copied? I'm hoping for the latter. With .NET you can set an option to scan all subfolders vs. the top directory. I don't know what it takes in C/C++.

If the file is not compressed and you just want to find, for example, all XML files, it only takes about 10 lines of code.