HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.
Pages: [1] 2 3 4

Author Topic: Basic SpecOracle setup  (Read 16452 times)

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Basic SpecOracle setup
« on: Mar 16, 16, 03:09:27 AM »

A word of caution:  While the Spec Oracle Tutorial should work fine, there is no complete
fully functional Spec Editor currently.  Theres an example being worked on however no
fully functional editor can be created without knowing what data types a spec contains.
When finished the example should offer a fully functional example using the very basic
data types that every spec should have, using a GUI that will need to be redesigned
by you.

This completed tutorial, including Spec Editor, can be found here:
http://wiki.anvilofhonor.com







NOTE: I've some tidying up to do when my eyes are uncrossed.  I will add the steps to create a basic editor
as well.


CORRECTION: In the meanwhile, there is no spec editor.. By default you don't have an item spec editor etc
My fault for not double checking my scripts against original versions.
Thanks Thaz and Jrome for catching these errors.
I've commented out lines that "should" cause issues with compiling.
I'll add the steps to create a basic spec editor, and then point out what to change in the
scripts below.

TIP: in the meanwhile, you can test your new spec oracle using the following chat command:
/heoracle open *MYORACLENAME*SpecOracle


If it makes you feel any better, once you create the default spec editor, it is then used as a base for all
of your various spec editors for any spec oracle you create (unless you just want to create a new one for
each :) )  Maybe in the next day or so I'll get it posted.


Abilities SpecOracle System:

This system can be adapted to any other

****************************
Auto Naming for your own class/system name:
If you so wish, you can do a find/replace all of the following keywords
to automatically fill in every place needed with the name(s) of your own classes etc.
You do NOT need to include the word "spec"/"specOracle" etc when replacing these.
Including the Scripts included below:
(note: include the "*" in the keyword to replace it)
*Ability* - replace with your own class name
*MYGAME* - replace with Game Specific Name or Abbreviation


This assumes you know how to use the DoM editors to create classes and fields.

OR if you're more comfortable with using the CLI console see JRome's post regarding this below.
« Last Edit: Mar 30, 16, 09:02:12 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #1 on: Mar 16, 16, 03:09:38 AM »

SERVER:

1 Create a new class called *Ability*Spec (Archetype = Data)
2 Select *Ability*Spec class and add BaseSpec as a parent class
3 Add the following fields: displayName and displayDescription
  Save
4 Create a new class called *Ability*SpecOracle (Archetype = Data)
5 select *Ability*SpecOracle class and add SpecOracle as a parent class
  Save
*****************
Optional:
Create a new Prototype for your class:
In the console panel type: \cpfc *Ability*SpecOracle *Ability*SpecOracle
*****************
6 Create a script for the new specOracle class:
   select *Ability*SpecOracle class and click Open Script
   select the template for SpecOracle or, if you don't see it
   Create an empty script (leave the name) then search the server script list
   for TemplateSpecOracle, open it, copy paste contents to your own.


7 Edit the new SpecOracle script as follows (or if you auto replaced the keywords as described above,
   copy the following code to your SpecOracleClassMethods script.)
   
//CODE: **************Server *Ability*SpecOracleClassMethods Script
Code: [Select]
// This is the template for a new SpecOracle class methods script, it is expected that each method
//   be overriden to implement your specific implementation.  Additional methods you may wish to
//   override are located in the SpecOracleClassMethods script, care should be exercised when overriding
//   these additional methods so that the core functionality remains intact for your new spec oracle.

method getValidBaseClasses() as List of String
// return a list of classes that prototypes for the spec can be created from
//
//
  valid as List of String
// add back YourBaseClass to valid
  add back "*Ability*Spec" to valid
  return valid
.

method getSpecDecoratorClasses() as List of String
// return a list of classes that can be glommed to prototypes for the spec
//   these classes are referred to as decorator classes and are used to
//   extend the functionality of a spec via composition
//

//this would be such as hasvalue, hasloot, equippable etc
  valid as List of String
  //Example list
  //add back Buff to valid
  //add back DeBuff to valid
  //add back Melee to valid
  //These are just examples, but would be valid class names of the SpecDecorator type.
  //These are not covered in this tutorial.
  return valid
.

method getSpecFileExtension() as String
// Each spec oracle should choose a unique file extension for their specs
//   failure to update this to your system's extension will result in your specs
//   being vulnerable to being overwritten by other specOracles that people did not
//   following instructions to choose their extension.
//
  return ".*Ability*Spec"
.

method getSpecOracleClass() as String
// Prototypes on the server should be named on the server the same as this name
//   client oracle nodes are retrieved on the client using the class name.
//
  return "*Ability*SpecOracle"
.

method getSpecNamePrefix() as String
// A unique prefix for this specOracle used in the naming of the prototypes (specs) created for this oracle
//   commonly this is the same as the name of the fundamental base class from which all specs in this oracle
//   are derived.
//
// Failure to implement this method with a unique prefix will result in errors when creating a new spec
//   because of name conflicts in the prototype namespace
//
  return "*Ability*Spec"
.

method getSpecClass() as String
// It is intended that each child class override this method with their own
//   this should return the fundamental base class for specs in this oracle.
//
  return "*Ability*Spec"
.

method _getSpecHeaderValuesForSpec( specKey as ID ) as List of String
  //KRUN: this allows for the use of the name and description fields of the spec to be displayed in the chooser
  values as List of String
  spec as NodeRef of Class baseSpec = me.getSpecByKey( specKey )
  foreach field in me.ListCollectionHeaders()
    when tolower( field )
      is "prototypename"
        // prototype name has a special status requiring special mechanics to get the value
        //add back getPrototypeName((getPrototypeByID( me.SpecKeyMap[specKey] ))) to values
        add back getPrototypeByID( me.SpecKeyMap[specKey] ).GetDisplayName() to values
      .
      is "prototypedescription"
        // prototype description has a special status requiring special mechanics to get the value
        //add back getPrototypeName((getPrototypeByID( me.SpecKeyMap[specKey] ))) to values
        add back getPrototypeByID( me.SpecKeyMap[specKey] ).GetDisplayDescription() to values
      .
      is "speckey"
        add back spec.SpecKey to values
      .
      default
         if HasField( spec, field )
          fieldType as String = tolower( getFieldType( field ))
          when fieldType
            is "string"
              str as String = spec.fieldCollection[ field ]
              add back str to values
            .
            is "float"
              f as Float = spec.fieldCollection[field]
              s as String = f  // autoconversion
              add back s to values
            .
            is "integer"
              i as Integer = spec.fieldCollection[field]
              s as String = i // autoconversion
              add back s to values
            .
            is "timeinterval"
              t as TimeInterval = spec.fieldCollection[field]
              S as String = t
              add back s to values
            .
            is "boolean"
              b as Boolean = spec.fieldcollection[field]
              s as String = b
              add back s to values
            .
            is "vector3"
              v as Vector3 = spec.fieldCollection[field]
              s as String = v
              add back s to values
            .
            is "id"
              i as ID = spec.fieldCollection[field]
              s as String = i
              add back s to values
            .
            is "datetime"
              d as DateTime = spec.fieldCollection[field]
              s as String = d
              add back s to values
            .
            default
              ScriptError( "Unhandled field type(" + fieldType + ") you must override this method and create a conversion from the explicit type to string." )
            .
          .
        else
          // field not present, add a default
          add back "ThisField(" + field + ") not a member of prototype(" + spec + ")" to values
        .
      .
    .
  .
 
  return values
.

method ListCollectionHeaders() as List of String
// This method is an override for the collectionHeader class method
//
//  By default, if no headers are set, we specify the speckey and prototype name as headers
//  KRUN: This makes the spec chooser GUI display the name and description of the spec
  headers as List of String
  if me.collectionHeaders.length = 0
    // use default of specKey and prototype Name
    add back "SpecKey" to headers
    add back "PrototypeName" to headers
    add back "PrototypeDescription" to headers   
  else
    headers = me.collectionHeaders   
    hasSpecKey as Boolean
    foreach h in me.collectionHeaders
      if tolower( h ) = "speckey"
        hasSpecKey = true
      .
    .
    if not hasSpecKey
      add front "SpecKey" to headers
    .
  .   
  return headers
.


//CODE END:****************************************************

Compile and submit *Ability*SpecOracleClassMethods
« Last Edit: Mar 16, 16, 03:11:26 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #2 on: Mar 16, 16, 03:09:47 AM »


7A OPTIONAL: If you will create objects/nodes from this spec class (SpecDerivedObjects) then do the following:
   in the Server DoM Editor create a new class called *Ability* (Archetype = Data)
   Select the *Ability* class and add the parent class SpecDerivedObject
   Save
   Click open script, select create empty script.
   Paste the following code into the new *Ability*ClassMethods script
   
//CODE:  ************ *Ability*ClassMethods Script
Code: [Select]
method getMySpecOracle() as NodeRef of Class SpecOracle
// It is intended that each child class override this method
//
// Replace "SpecOracle" with your spec oracle's class, which should also be the name of your spec oracle prototype
//

// Your spec oracle prototype's name should be the same as the class from which it was created (by convention)
//   for example, _FxSpecOracle.
//
  return getPrototype( "*Ability*SpecOracle" )
.

//CODE END:********************

Compile and submit *Ability*ClassMethods
« Last Edit: Mar 16, 16, 03:11:51 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #3 on: Mar 16, 16, 03:09:57 AM »

8 Create the SpecClassMethods script:
   in the Server DoM Editor select the *Ability*Spec class and click open script.
   select the BaseSpec template or if not shown, Create an empty script and
   search the server scripts and open the baseSpecTemplate
   copy paste its contents into your *Ability*SpecClassMethods script, OR
   Copy the following code into your SpecClassMethods script.
   
//CODE: ************ *Ability*SpecClassMethods Script

Code: [Select]
method CreateFromSpec() as NodeRef

//NOTE:Krun: comment the following return ONLY if you plan to use SpecDerivedObjects using this spec type
//if so make sure you followed step 7A above.

RETURN

// This will always need to be overridden
//
  aInstantiatedNode as NodeRef
  // a thingSpec might want to create thing nodes, itemspecs if you have such a thing might create item nodes
  //If you followed step 7A above and wish to use SpecDerivedObjects uncomment the following line
  //aInstantiatedNode = CreatePersistedNodeFromClass( "*Ability*" )
 
  // Make sure your createFromSpec ALWAYS calls the method
  //    OnCreateNotifySpecDecoratorClasses to allow decorators a chance to initialize the object
  me.OnCreateNotifySpecDecoratorClasses( aInstantiatedNode )
 
  return aInstantiatedNode
.

method _LibraryCreateFromSpec( libraryCmd as String ) as NodeRef
// This will always need to be overridden
//
  aInstantiatedNode as NodeRef
  // Typically, when creating library objects using the spec system the nodes already exist
  //   so the spec isn't normally a factory for the node itself (i.e. it doesn't create a node)
  //   but it does often add additional classes and/or create additional nodes, associatiate things
  //   together and so on
 
  // Make sure your _LibraryCreateFromSpec ALWAYS calls the method
  //    _OnLibraryCreateNotifySpecDecoratorClasses to allow decorators a chance to initialize the object
 
  me._OnLibraryCreateNotifySpecDecoratorClasses( aInstantiatedNode, libraryCmd )
 
  return aInstantiatedNode 


method getMySpecOracle() as NodeRef of Class SpecOracle
// Specs need to know how to find their specOracle
//   on the server the oracle is represented by a prototype
//   which is created (by convention) using the same name as the classname
//
// It is critical this method be implemented and return your specOracle node
//
  return getPrototype( "*Ability*SpecOracle" )
.

// Called following instantiation of seamless link via the library command.  The spec class has utility methods to parse this command
//   but you are free to parse custom parameters however you need.
shared function _OnLibraryInstantiationFromSpec( spec as NodeRef of Class baseSpec, derivedObject as NodeRef of Class SpecDerivedObject, librarycmd as String )
// Shared function called in the base and decorator classes of a spec when an object is instantiated
//   do any initialization/setup required by the decorator class
//
// This is typically used to GLOM helper classes that support the functionality of the spec's
//   base or decorator classes.
//

.

shared function OnInstantiationFromSpec( derivedObject as NodeRef )
// Shared function called in the base and decorator classes of a spec when an object is instantiated
//   do any initialization/setup required by the decorator class
//
// This is typically used to GLOM helper classes that support the functionality of the spec's
//   base or decorator classes.
//
.

method NotifyOnNewSpecCreation()
//------------------------------------------------------------------
// Called after a new spec has been returned from prototype creation
// and is ready for editing.  "default" values could be initialized here
//------------------------------------------------------------------

.

method OnlyMarshalSpecifiedFieldsToRepository() as Boolean
// If a spec returns true, only the specified fields will be marshaled
//   to the file in the repository otherwise the entire node is marshalled.
//
// The shared function OnMarshalClassSpecifiedFields in each of the class method
//   scripts on the spec dtermines which fields will be marshaled if you return true
//
  return false
.

shared function OnMarshalClassSpecifiedFields( n as NodeRef, marshalString references String )
// Called by the MarshalNodeWithClassSpecifiedFields and MarshalPrototypeWithClassSpecifiedFields in the classMethods
//   scripts for the node n.  Your class method script should use marshalNodeAppendField to add the specific
//   fields you want.
//
//  MarshalAppendField( marshalString, n, "MyField" )
.
//CODE: END **********************************

Compile and submit *Ability*SpecClassMethods

This concludes the server portion of the setup.
« Last Edit: Mar 16, 16, 03:12:40 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #4 on: Mar 16, 16, 03:10:08 AM »

CLIENT:

EDIT NOTE: I've altered the AbilitySpecClassMethods script InvokeSpecEditorGUI to now
work with a default properties editor.
You can ADD and EDIT specs using this default window (which is the default nodeProperties editor)
Until i finish putting together the GUI and scripts for the SpecEditor.
You need only replace the InvokeSpecEditorGUI method if you have an older copy.

1 Create a new class called *Ability*Spec (Archetype = Data)
2 Select *Ability*Spec class and add BaseSpec as a parent class
3 Add the following fields: displayName and displayDescription
Save
4 Click Open Script and select Create empty script.
   Copy Paste the following code into your new *Ability*SpecClassMethods script
   If you did NOT use the auto replace by keywords above, you must edit the names in "*" to match your own.
   
//CODE:********  *Ability*SpecClassMethods script
Code: [Select]
method InvokeSpecEditorGUI( parentGUI as ID ) as NodeRef of Class GUIControl 
  gui as NodeRef of Class GUIControl
  if $GUI._ShiftIsPressed() //basic field view
    window as NodeRef of Class GUIControl = CreateNodeFromPrototype("_NodePropertyEditorWindow")
    window.build = true
 
    glomClass("SpecOraclePropertyEditor", window)
    where window is kindof SpecOraclePropertyEditor
      window.SOPEparentGUI = parentGUI
      window.SOPEtype = me.GetMySpecOracle().getSpecOracleClass()
      window.NPEnode = me
    .
 
    window._changeWindowTitleText( "Spec Editor - " + getPrimaryClassOnNode( me ) + "(" + me.SpecKey + ")" )
 
    var editor = FindGUIControlByName(window.getClientarea(), "nodepropertyeditor")
 
    editor._setNodePropertyEditorEditNode(me)
    editor._allowTitlebar(false)

    editor.centerControlOver(None)
    return editor
  else
    if ClassExists("*Ability*SpecEditorGUI")
                //the following class would not yet have been created so ive commented them out for now.
                //these would be implemented in the creation of your spec editor class and script
//gui = CreateNodeFromPrototype("*Ability*SpecEditorGUI")
//gui.build = true
//gui.SetNodeCollectionInterfaceWindowNode(me)
//gui.SetSpecOraclePropertyEditorType(me.GetMySpecOracle().getSpecOracleClass())
//gui.SetSpecOraclePropertyEditorParent(parentGUI)
else
          window as NodeRef of Class GUIControl = CreateNodeFromPrototype("_NodePropertyEditorWindow")
          window.build = true
 
          glomClass("SpecOraclePropertyEditor", window)
          where window is kindof SpecOraclePropertyEditor
            window.SOPEparentGUI = parentGUI
            window.SOPEtype = me.GetMySpecOracle().getSpecOracleClass()
            window.NPEnode = me
          .
 
          window._changeWindowTitleText( "Spec Editor - " + getPrimaryClassOnNode( me ) + "(" + me.SpecKey + ")" )
 
          var editor = FindGUIControlByName(window.getClientarea(), "nodepropertyeditor")
 
          editor._setNodePropertyEditorEditNode(me)
          editor._allowTitlebar(false)

          editor.centerControlOver(None)
          return editor
.
  .
  return gui
.

//use this for building spec specific panels within the editor after it is built
shared function BuildSpecEditorPanel(spec as NodeRef) as NodeRef of Class GUIControl
  println(SYSTEM.EXEC.CALLEDBYSCRIPT)
  gui as NodeRef of Class GUIControl
  if ClassExists("*Ability*SpecEditorMainPanel")
        //the following is commented out as you would not have this class methods script created yet
        //I'll cover it in the spec editor setup tutorial
//gui = CreateNodeFromPrototype("*Ability*SpecEditorMainPanel")
//gui.build = true
        //the following would be implemented in your spec editor class methods script
        //gui.PopulateSpecEditorFields(spec)
  .
  return gui
.

method getMySpecOracle() as NodeRef of Class SpecOracle
// It is intended that each child class override this method
//
// Replace "DefaultSpecOracle" with your spec oracle's class

  return $SPECORACLEUTILS._GetSpecOracleByClass( "*Ability*SpecOracle" )
.

method CreateFromSpec() as NodeRef
// This will always need to be overridden
//
// You should replace DefaultSpecDerivedObject with the class your spec uses to factory its objects.
//
  assert( me.SpecLoadStatus = READY, "You can not use a spec to factory an instantiation until it is loaded from the repository." )

  aInstantiatedNode as NodeRef = CreateNodeFromClass( "*Ability*" )
  // for example, an weaponSpec might create an instantiation of class weapon
 
  // Make sure your createFromSpec ALWAYS calls the method
  //    OnCreateNotifySpecDecoratorClasses to allow decorators a chance to initialize the object
  me.OnCreateNotifySpecDecoratorClasses( aInstantiatedNode )
 
  return aInstantiatedNode
.

method GetMyName() as String
  return me.displayName
.

method SetMyName(name as String)
  me.displayName = name
.

method GetMyDescription() as String
  return me.DisplayDescription
.

method SetMyDescription(desc as String )
  me.DisplayDescription = desc
.
//CODE END:******************

Compile and submit

4 Create a new class called *Ability*SpecOracle (Archetype = Data)
5 select *Ability*SpecOracle class and add SpecOracle as a parent class
6 Add CollectionOrderedSet to *Ability*SpecOracle as a parent class
7 Add ObsSubject to *Ability*SpecOracle as a parent class
Save
8 Click Open Script and select create empty script
   Copy and paste the following Code into your new *Ability*SpecOracleClassMethods script.
   If you did NOT use the auto replace by keywords above, you must edit the names in "*" to match your own.
   
//CODE:*************** *Ability*SpecOracleClassMethods Script

Code: [Select]
method onOracleInstantiation()
// Called when an oracle node is first created through the $SPECORACLEUTILS class methods that provide singleton
//   behaviors
//

.

method getSpecFileExtension() as String
// It is intended that each child class override this method with their own
//   providing the file extension the server specOracle class declared
//
// Replace ".defaultSpec" with your spec oracle's file extension as defined on the server's spec oracle
//
  return ".*Ability*Spec"
.

method getSpecOracleClass() as String
// It is intended that each child class override this method with their own
//
// Prototypes on the server should be named on the server the same as this name
//   client oracle nodes in the ClientDataSystem should be named identically.
//
// Replace "DefaultSpecOracle" with your spec oracle's class

  return "*Ability*SpecOracle"
.

method getSpecClass() as String
// It is intended that each child class override this method with their own
//   to return the fundamental class (often abstract) class all of the oracles spec
//   share
//
// Replace "defaultSpec" with your spec oracle's fundamental spec class
//

  return "*Ability*Spec"
.

function RepositoryDataDownloaded(name as String, requestTag as ID, successful as Boolean, data as String) as Boolean
// This function must exist in each child class methods script because it is called as a result of requestSpecData
//   The only functionality it requires is retrieval of the oracle node so the method may be called on it
//
// Replace "DefaultSpecOracle" with your spec oracle's class
//
 
  // retrive the proper oracle node for this class
  oracle as NodeRef of Class SpecOracle = $SPECORACLEUTILS._GetSpecOracleByClass( "*Ability*SpecOracle" )

  return oracle.onRepositoryDataDownloaded( name, requestTag, successful, data )
.
//CODE END:***********************

Compile and submit

OPTIONAL: If you will create objects/nodes from this spec class (SpecDerivedObjects) then do the following:
   in the Client DoM Editor create a new class called *Ability* (Archetype = Data)
   Select the *Ability* class and add the parent class SpecDerivedObject
   Click Open Script and choose create empty script.
   Copy and paste the code below into the new *Ability*ClassMethods script

   Note: Ive left a few Marshal functions which i use here as they are useful, as a reference
   for spec derived objects

//CODE:  ********** *Ability*ClassMethods Script
Code: [Select]
method getMySpecOracle() as NodeRef of Class SpecOracle
// It is intended that each child class override this method, replacing the "DefaultSpecOracle" with the class
//   from which your spec oracle derives.
//
// Replace "DefaultSpecOracle" with the class for your spec oracle

  return $SPECORACLEUTILS._GetSpecOracleByClass( "*Ability*SpecOracle" )
.


//node does not exist, was added
shared function OnUnmarshalNodeInstantiation( theNode as NodeRef of Class *Ability* )
  //println("instantiated *ability* node")
 
.
//node exists , update it
shared function OnUnmarshalNode(theNode as NodeRef of Class *Ability* )
  //println("*Ability* node unmarshalled")
 //this is a good place to make associations etc if need be when the node is unmarshalled
.

shared function _OnReplicationFieldUpdated(updateNode as NodeRef, updateField as String)
  //println("got something")
  where updateNode is kindof *Ability*
    when ToLower(updateField)
      is "myFieldHere"
          //fancy things here
      .
    .
  .
.

method _OnReplicationNodeAdded(addedNode as NodeRef)
  where addedNode is kindof *Ability* 
  //  println("*ABILITY*: on replication node added")
   
  .
.

//CODE END:***************


//NOTE: This only needs to be done ONCE, if you HAVE a custom SpecOracleUtils class already IGNORE THIS!!
FINAL: CLIENT: Create a custom SpecOracleUtils Class and script (Archetype = Data)


Now to tie it all together:
CLIENT: In the CLIENT DOM create a new class called *MYGAME*SpecOracleUtils
    Save
    Click open script and choose create empty
    copy and paste the following code into your new *MYGAME*SpecOracleUtils script
Code: [Select]
method GetItemSpecOracle() as NodeRef of Class _ItemSpecOracle
  itemSpecOracle as NodeRef of Class _itemSpecOracle = $SPECORACLEUTILS._GetSpecOracleByClass( "_itemSpecOracle" )
 
  return itemSpecOracle
.

method Get*Ability*SpecOracle() as NodeRef of Class *Ability*SpecOracle
  aso as NodeRef of Class *Ability*SpecOracle = $SPECORACLEUTILS._GetSpecOracleByClass("*Ability*SpecOracle")
  return aso
.


method HE_GetAllSpecOracles( oracles references List of ID ) as Boolean
// This method is called to retrieve all of the oracles of which the spec system should be
//    aware.  The default GUIs and a variety of utility functions depend on your oracle
//    being included in this list
//
  add back me.GetItemSpecOracle() to oracles
  add back me.Get*Ability*SpecOracle() to oracles
  return true
.
method HE_GetSpecOracleByClass( oracleClass as String, specOracle references NodeRef of Class SpecOracle ) as Boolean
 
  Assert( ClassExists( oracleClass ), "Invalid class name provided to GetSpecOracleByClass")
 
  oracles as List of NodeRef of Class SpecOracle = $SPECORACLEUTILS._GetKnownSpecOracles()
 
  oTest as NodeRef
  foreach o in oracles
    otest = o
    when toLower( oracleClass )
      is "*ability*specoracle"
        if otest is kindof *Ability*SpecOracle
          specOracle = otest
          break
        .
      .
      is "itemspecoracle"
        if otest is kindof _itemSpecOracle
          specOracle = otest
          break
        .
      .
    .
  .
  if specOracle <> None
    return true
  .
 
  return false
.

Compile and submit
   in the BLADE hit the F5 key
   Select the TOOLS tab
   Select System Nodes Configuration GUI
   Select the CLIENT radio button
   from the drop down select SpecOracleUtils
   where it says Name of Class to be Added type
   *MYGAME*SpecOracleUtils
   Click ADD
   Close the System Nodes Configuration GUI menu
   
Now your *Ability*SpecOracle class is setup and ready to use,
As well as your Custom SpecOracleUtils


NOTE: To come: Setting up a basic Spec Editor
« Last Edit: Mar 18, 16, 08:08:02 AM by ToY-Krun »
Logged

Jrome90

  • General Accounts
  • *
  • Posts: 330
    • View Profile
Re: Basic SpecOracle setup
« Reply #5 on: Mar 16, 16, 08:02:56 PM »

Thanks for posting this Krun. I am sure many will find this useful.
It looks like you forgot to add the "SpecDerivedObject" class to the client side.
For those that prefer to use the CLI (Like me) here are the CLI commands that correspond to each step that is done in the DOM editor

Server

1 Create a new class called *Ability*Spec (Archetype = Data)
\CCD *Ability*Spec, data; description="*Ability*Spec"

2 Select *Ability*Spec class and add BaseSpec as a parent class
\MCDAP "*Ability*Spec"; "baseSpec"

3 Add the following fields: displayName and displayDescription
\MCDAF  *Ability*Spec; displayName DisplayDescription

4 Create a new class called *Ability*SpecOracle (Archetype = Data)
\CCD *Ability*SpecOracle, data; description="*Ability*SpecOracle"

5 select *Ability*SpecOracle class and add SpecOracle as a parent class
\MCDAP "*Ability*SpecOracle"; "SpecOracle"


7A OPTIONAL: If you will create objects/nodes from this spec class (SpecDerivedObjects) then do the following:
   in the Server DoM Editor create a new class called *Ability* (Archetype = Data)
   Select the *Ability* class and add the parent class SpecDerivedObject

\CCD *Ability*, data; description="SpecDerived Object for *Ability*
\MCDAP "*Ability*"; "SpecDerivedObject"


Client

1 Create a new class called *Ability*Spec (Archetype = Data)
|CCD *Ability*Spec, data; description="*Ability*Spec"

2 Select *Ability*Spec class and add BaseSpec as a parent class
|MCDAP "*Ability*Spec"; "baseSpec"

3 Add the following fields: displayName and displayDescription
|MCDAF  *Ability*Spec; displayName DisplayDescription

4 Create a new class called *Ability*SpecOracle (Archetype = Data)
|CCD *Ability*SpecOracle, data; description="*Ability*SpecOracle"

5 select *Ability*SpecOracle class and add SpecOracle as a parent class
6 Add CollectionOrderedSet to *Ability*SpecOracle as a parent class
7 Add ObsSubject to *Ability*SpecOracle as a parent class
|MCDAP "*Ability*SpecOracle"; "SpecOracle" "CollectionOrderedSet" "ObsSubject"


OPTIONAL: If you will create objects/nodes from this spec class (SpecDerivedObjects) then do the following:
   in the Client DoM Editor create a new class called *Ability* (Archetype = Data)
   Select the *Ability* class and add the parent class SpecDerivedObject

|CCD *Ability*, data; description="SpecDerived Object for *Ability*
|MCDAP "*Ability*"; "SpecDerivedObject"

« Last Edit: Mar 16, 16, 08:12:21 PM by Jrome90 »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #6 on: Mar 16, 16, 09:24:34 PM »

Thanks Jrome, i was getting cross eyed last night.  I missed a few other custom classes too, Thanks Thaz for pointing them out :)

I've removed the references to the custom fields abName/abDescription and replaced them with the proper default
displayName and displayDescription.  Thanks for catching that.

I planned to account for those when doing an additional setup of the spec editors themselves, but i forgot to add a "ClassExists()" check for several. 

Will make those changes.

Also thanks for the CLI commands, I know alot of folks might rather have those handy than using the DoM :)

If any other problems are spotted please let me know.

Thanks again!
« Last Edit: Mar 16, 16, 09:53:51 PM by ToY-Krun »
Logged

Thazager

  • General Accounts
  • *
  • Posts: 1160
  • Never stop learning
    • View Profile
Re: Basic SpecOracle setup
« Reply #7 on: Mar 17, 16, 10:52:48 PM »

A few things you might add:

How does the value in the spec effect the data? (is there a direct connection, or does the table that is created need to get called each time its needed, like a tooltip hover over an item with item specs)

How to add different types of values for the user, like adding a text input box (strings), adding a number input box (integers, floats), adding a drop down list (list of enums), or others.

How does one show a popup based on a GUI texture chosen for a picture? (user sets X (0-9), Y (0-9) offset and the popup shows the 32x32 pic chosen).

How does one change which values are displayed, (based on 1 of the choices) to the spec editor GUI. (or would that be something like hide/show fields?)

 (or might these be in: "NOTE: To come: Setting up a basic Spec Editor")
« Last Edit: Mar 17, 16, 11:17:25 PM by Thazager »
Logged
Lead scripter for EO, Repop helper.
HSL Video tutorials:
https://community.heroengine.com/forums/index.php/topic,1719.msg36858.html#msg3685

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #8 on: Mar 18, 16, 06:23:17 AM »

Hey Thazager,

Yeah that is all based on the editor.

I want to do a step by step for it but I want to make sure it includes classes/controls that everyone will have available.

I'll do an explanation as well as to how the info is stored to a spec via the editor and how its retrieved both client and
server side.

I'll work on the GUI for it today and should then have something to post up.

Theres actually alot more involved with the editor than the oracle itself :)

If i start giving examples i'll confuse the issue im afraid, so ill just get busy on the step by step.
« Last Edit: Mar 18, 16, 06:27:38 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #9 on: Mar 18, 16, 08:18:17 AM »

I've replaced the InvokeSpecEditorGUI method in the CLIENT section (*Ability*SpecClassMethods)
If you've NOT already copied the entire script, see Reply#4 for the entire updated script.

Code: [Select]
method InvokeSpecEditorGUI( parentGUI as ID ) as NodeRef of Class GUIControl
  gui as NodeRef of Class GUIControl
  if $GUI._ShiftIsPressed() //basic field view
    window as NodeRef of Class GUIControl = CreateNodeFromPrototype("_NodePropertyEditorWindow")
    window.build = true
 
    glomClass("SpecOraclePropertyEditor", window)
    where window is kindof SpecOraclePropertyEditor
      window.SOPEparentGUI = parentGUI
      window.SOPEtype = me.GetMySpecOracle().getSpecOracleClass()
      window.NPEnode = me
    .
 
    window._changeWindowTitleText( "Spec Editor - " + getPrimaryClassOnNode( me ) + "(" + me.SpecKey + ")" )
 
    var editor = FindGUIControlByName(window.getClientarea(), "nodepropertyeditor")
 
    editor._setNodePropertyEditorEditNode(me)
    editor._allowTitlebar(false)

    editor.centerControlOver(None)
    return editor
  else
    if ClassExists("*Ability*SpecEditorGUI")
                //the following class would not yet have been created so ive commented them out for now.
                //these would be implemented in the creation of your spec editor class and script
//gui = CreateNodeFromPrototype("*Ability*SpecEditorGUI")
//gui.build = true
//gui.SetNodeCollectionInterfaceWindowNode(me)
//gui.SetSpecOraclePropertyEditorType(me.GetMySpecOracle().getSpecOracleClass())
//gui.SetSpecOraclePropertyEditorParent(parentGUI)
else
          window as NodeRef of Class GUIControl = CreateNodeFromPrototype("_NodePropertyEditorWindow")
          window.build = true
 
          glomClass("SpecOraclePropertyEditor", window)
          where window is kindof SpecOraclePropertyEditor
            window.SOPEparentGUI = parentGUI
            window.SOPEtype = me.GetMySpecOracle().getSpecOracleClass()
            window.NPEnode = me
          .
 
          window._changeWindowTitleText( "Spec Editor - " + getPrimaryClassOnNode( me ) + "(" + me.SpecKey + ")" )
 
          var editor = FindGUIControlByName(window.getClientarea(), "nodepropertyeditor")
 
          editor._setNodePropertyEditorEditNode(me)
          editor._allowTitlebar(false)

          editor.centerControlOver(None)
          return editor
.
  .
  return gui
.

copy and paste that method and replace it in your own script (if you have already copied the old version)
and compile/submit it.

it now should allow you to add/edit specs using the default nodePropertyEditor panel.

To do so, open your spec oracle (/heoracle open *Ability*specoracle) and Add or double click on a spec.

It is limited as it wont allow editing lists and doesnt have an icon chooser, but that will be added and any/all fields can be changed/updated once you have a proper editor working.


Also, a heads up.

You can go ahead and add fields that you know you will need for your spec.

In the SERVER AND CLIENT DoM's, open the SpecClass for your spec:
Example *Ability*Spec
And add any fields relevant to your spec class, on both server and client.
Make SURE both Server and Client have the same fields and they are the same field type.
Easiest way is to create all the fields on the server or client, add them to the class, then
copy them to the server/client and add them there as well.
Do not set replication as specs are not replicated.  simply add the fields to the class.

Having done that, your spec is ready to use.
You can then open the Client side Script for your Spec Class and add methods
(not functions) for getting/setting each of your fields that you added.
These will be called from the GUI once its completed.
NOTE: the default editor panel will automatically retrieve and save your fields if you use it to edit.
I advise adding the methods so that you can retrieve the information for other systems/scripts when
needed in the future.

On the server, in the spec class methods for your spec, you can also add the same methods
such as GetmyName(), GetMyIconID() etc, whatever you need.

Remember from this point on until we get to specDerivedObjects, we're dealing ONLY with the
SpecClass (*Ability*Spec)  or whatever you have named it.
« Last Edit: Mar 18, 16, 08:20:30 AM by ToY-Krun »
Logged

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #10 on: Mar 21, 16, 05:53:30 PM »

This has been moved to the wiki http://wiki.anvilofhonor.com
including the Spec Editor.

If you have problems or notice errors (or have ideas regarding the layout) let me know
« Last Edit: Mar 21, 16, 07:23:07 PM by ToY-Krun »
Logged

Thazager

  • General Accounts
  • *
  • Posts: 1160
  • Never stop learning
    • View Profile
Re: Basic SpecOracle setup
« Reply #11 on: Mar 21, 16, 08:17:09 PM »

working on tutorial, thanks

2 - Create a new class called *myPrefix*_BaseWindow
(from the info below it, guessing archtype gui)

In a function call, does the node "me" have the correct value (offset)?  (usually need a method for that node.)
Code: [Select]
function GetTopControl() as NodeRef of Class GUIControl
  return FindGUIControlByName(me, "header")
.

method GetTopControl() as NodeRef of Class GUIControl
  return FindGUIControlByName(me, "header")
.

PreRequisite SpecClass Methods ... add at least the following METHODS
(the list shows, but not the links ... yet?)

*(will add possibly other stuff on this post as I work thru it)
« Last Edit: Mar 21, 16, 08:46:33 PM by Thazager »
Logged
Lead scripter for EO, Repop helper.
HSL Video tutorials:
https://community.heroengine.com/forums/index.php/topic,1719.msg36858.html#msg3685

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #12 on: Mar 21, 16, 08:29:53 PM »

Thanks Thaz,
This has been alot of info to sort through, appreciate you pointing out anything you find issue with.

Added (Archetype = GUIControl)

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #13 on: Mar 21, 16, 11:22:31 PM »

A note of explanation about the IconChooserGUI and how it chooses an Icon from the IconAtlas Texture....


I dont claim to be an artist, modeler, or perfectly know anything about what im about to say... :)


But as I understand it, the code that specifies the size of the icons and the texture, reads the atlas
and when you click on it, it takes the screen position of the window, that you clicked on, and using the
coded size of the icons (96X96) it maps out that atlas, and then depending on where you clicked, it takes the
image in that "slot" and uses that image as your icon.

No more than i know about that, its as close to an explaination as i can give heh.

I do know that if you choose larger/smaller icons on that sheet, then you will need to alter the code
for "GetTextureSize()"  (i think i have that function right) which is a shared function.
so you can create atlases of any size icons, you just have to tell the functions what size they're supposed to be
so that it "picks" the right icon, at the right coordinates.

Maybe Bennett can shed some light on that if he has time.

ToY-Krun

  • General Accounts
  • *
  • Posts: 677
  • Support Volunteer
    • View Profile
Re: Basic SpecOracle setup
« Reply #14 on: Mar 21, 16, 11:31:09 PM »

An added note, in the CLIENT script *Ability*SpecClassMethods

in the InvokeSpecEditorGUI()
Uncomment the lines regarding the *Ability*EditorMainPanel
ONCE you have completed setting up the Spec Editor GUI http://wiki.anvilofhonor.com/wiki/index.php/Spec_Editor_Tutorial
Pages: [1] 2 3 4