Dynamic Types

Freeform discussion about anything related to modding Transcendence.
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

I'm working on Betel's evalXML suggestion: http://wiki.neurohack.com/transcendence/trac/ticket/737

I've come up with a design for a feature I call "Dynamic Types" and I'd love some feedback:

The goal of Dymanic Types is to allow an extension to create new types at runtime. For example, this might allow a mod to create a weapon with random properties. As with Betel's suggestion, the way this works is by providing an XML string that describes the type (using all the normal syntax, including embedded code) and adding it as a new type at runtime.

To implement this, I propose the following:

TEMPLATE TYPE

Imagine syntax like this:

Code: Select all

<TemplateType UNID="&itRandomWeapon;">
   <StaticData>
      <Template><![CDATA[
         <ItemType
            name="%itemName%"
            ...
            >
            <Weapon
               damage="%itemDamage%"
               ...
               >
               %itemEffect%
            </Weapon>
         </ItemType>
      ]]></Template>
   </StaticData>

   <Events>
      <OnEval><![CDATA[
           (subst (typGetStaticData &itRandomWeapon; "Template")
              {
                 itemName : (random '("Arguil cannon" "Brodian lancer" "Catapon weapon"))
                 itemDamage : (random '("damage='laser:3d6'" "damage='particle:1d8'"))
                 itemEffect : (random '(
                    "<Effect><Beam beamType='laser' primaryColor='255, 255, 0'/></Effect>"
                    "<Effect><Beam beamType='laser' primaryColor='0, 255, 255'/></Effect>"
                    ))
                 }
              )
      ]]></OnEval>
   </Events>
</TemplateType>
At game-creation time (before we bind UNIDs to structures) we iterate over all template types and evaluate them. In the example above, the UNID "&itRandomWeapon;" will be initialized to the result of the evaluation of the OnEval code. The semantics of OnEval are simple: we expect it to return a string of well-formed XML and we load it as if it were any other type.

Doing this at game creation time has a few advantages:

1. You can use the UNID (&itRandomWeapon;) in any other type (e.g., ship classes) as if it were a normal type.
2. I think you can specify an existing UNID (e.g., &itLaserCannon;) which will cause the dynamic type to override the existing one. [there might be roadblocks I haven't thought about.]
3. You can generate types (such as SystemMap) that are only used at game-creation time.

[A few notes of the above: First notice that I use the CDATA syntax. That's just standard XML syntax so that we don't have to escape all the angle-brackets (that works in today's XML parser). Second, notice the subst command takes a struct of key/value pairs--this makes the syntax easier, IMHO, and is something that I think I will add in 1.08.]

DYNAMIC TYPE FUNCTIONS

I think the above handles 80% of the use-case, and if people agree, the above might be all I get done in 1.08. But another possible use case is to create types in the middle of the game. For example, imagine that a quest needs to generate a weapon based on an item that the player brings.

We can add the following functions to help:

(unvCreateType UNID string) -> True/Nil

This function takes an UNID (see below) and a string. The string is just some well-formed XML string that could be generated using the subst technique above. If successful, this will register a new type associated with the given UNID. You can then use the UNID in any function that accepts it (e.g., itmCreate).

The type that you create will be persisted with the game.

There are a few limitations:

1. The UNID must be unused (thus, you cannot override a type using this technique, even one that was previously created with the function)
2. The kinds of types that you can create is more limited. E.g., you cannot create a SystemType with this technique.

To help generate UNIDs, we need a new functions:

(unvDynamicUNID string) -> UNID

This function works as follows: If "string" has not yet been bound, it generates a new UNID (from the 0xF??????? space) and associates the string with it. If the string has already been bound, it just returns the UNID for that string.

You can now use this function to create new types without having to worry about UNIDs. For example, you can do something like:

(unvCreateType
(unvDynamicUNID "myNewAmazingWeapon")
XMLforNewWeapon
)

and then:

(itmCreate (unvDynamicUNID "myNewAmazingWeapon") 1)

I'd love to hear feedback on this design. As I said, I'm leaning towards implementing template types for 1.08 and leaving the dynamic type functions for later, but I'm happy to hear counter-arguments.
User avatar
Betelgeuse
Fleet Officer
Fleet Officer
Posts: 1920
Joined: Sun Mar 05, 2006 6:31 am

hmm I like it but have a couple questions.

Would storage.xml be loaded before this allowing us access to data saved there?
Would we be able to use typSetGlobalData or something else to communicate between different evaluations? (other than globals of course)
I am assuming that it isn't saved in the saved game is this correct?, or do you load the save game when the game first runs so we can do the title screen ships.
Crying is not a proper retort!
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

Betelgeuse wrote:hmm I like it but have a couple questions.

Would storage.xml be loaded before this allowing us access to data saved there?
Would we be able to use typSetGlobalData or something else to communicate between different evaluations? (other than globals of course)
I am assuming that it isn't saved in the saved game is this correct?, or do you load the save game when the game first runs so we can do the title screen ships.
1. Yes, Storage.xml is loaded first, so you can use it.
2. typGet/SetGlobalData is a little tricky. You can definitely call it on static types; you definitely can't call it on dynamic types that have not yet been defined. It will probably fail if you try to call it for your own type in the middle of OnEval (but I'm not sure).
3. The dynamic types are saved along with the game, so you don't have to re-create them. In fact, you'll get an error if you try to redefine them.
User avatar
Aury
Fleet Admiral
Fleet Admiral
Posts: 5421
Joined: Tue Feb 05, 2008 1:10 am
Location: Somewhere in the Frontier on a Hycrotan station, working on new ships.

:D

To say the least, this is very, very cool. I can do some more fancy things for wyvera & the tinkers (TSB) with this.
(shpOrder gPlayership 'barrelRoll)
(plySetGenome gPlayer (list 'Varalyn 'nonBinary))
Homelab Servers: Xeon Silver 4110, 16GB | Via Quadcore C4650, 16GB | Athlon 200GE, 8GB | i7 7800X, 32GB | Threadripper 1950X, 32GB | Atom x5 8350, 4GB | Opteron 8174, 16GB | Xeon E5 2620 v3, 8GB | 2x Xeon Silver 4116, 96GB, 2x 1080ti | i7 8700, 32GB, 6500XT
Workstations & Render machines: Threadripper 3990X, 128GB, 6900XT | Threadripper 2990WX, 32GB, 1080ti | Xeon Platinum 8173M, 48GB, 1070ti | R9 3900X, 16GB, Vega64 | 2x E5 2430L v2, 24GB, 970 | R7 3700X, 32GB, A6000
Gaming Systems: R9 5950X, 32GB, 6700XT
Office Systems: Xeon 5318Y, 256GB, A4000
Misc Systems: R5 3500U, 20GB | R5 2400G, 16GB | i5 7640X, 16GB, Vega56 | E5 2620, 8GB, R5 260 | P4 1.8ghz, 0.75GB, Voodoo 5 5500 | Athlon 64 x2 4400+, 1.5GB, FX 5800 Ultra | Pentium D 3.2ghz, 4GB, 7600gt | Celeron g460, 8GB, 730gt | 2x Athlon FX 74, 8GB, 8800gts 512 | FX 9590, 16GB, R9 295x2 | E350, 8GB | Phenom X4 2.6ghz, 16GB, 8800gt | random core2 duo/atom/i5/i7 laptops
User avatar
Betelgeuse
Fleet Officer
Fleet Officer
Posts: 1920
Joined: Sun Mar 05, 2006 6:31 am

one question, what happens when you start a new game when your old game had dynamic types defined? Would it be locked into those? Or would things that are dynamic type dependent not be on the title screen (then when are they bound)?
Crying is not a proper retort!
User avatar
alterecco
Fleet Officer
Fleet Officer
Posts: 1658
Joined: Wed Jan 14, 2009 3:08 am
Location: Previously enslaved by the Iocrym

This looks very cool George. What a lot of fun we can have with this! :)

Personally I think the dynamic type functions sound most exciting, but I have no argument for why we should have those first. One question I do have about them though, is if the xml you must provide can be a string. So, "<ItemType ...>...</ItemType>"?

Other than that I can not think of anything that has not already been brought up. Except that, wow, I seriously need to get modding again once 1.08 comes out. So many cool new toys. Thanks! XD
Get your own Galactic Omni Device
Get it now. It's free!!
Image
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

Betelgeuse wrote:one question, what happens when you start a new game when your old game had dynamic types defined? Would it be locked into those? Or would things that are dynamic type dependent not be on the title screen (then when are they bound)?
When you press "New Game" all dynamic types are reset to initial conditions (whatever the adventure/extensions define).
When you press "Load Game" the dynamic types are initialized to whatever the saved game specifies.
[That is, dynamic types are bound to the game and do not "leak" from one game to another. If you want to share dynamic types across games then you can use Storage.xml for that.]
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

alterecco wrote:This looks very cool George. What a lot of fun we can have with this! :)

Personally I think the dynamic type functions sound most exciting, but I have no argument for why we should have those first. One question I do have about them though, is if the xml you must provide can be a string. So, "<ItemType ...>...</ItemType>"?

Other than that I can not think of anything that has not already been brought up. Except that, wow, I seriously need to get modding again once 1.08 comes out. So many cool new toys. Thanks! XD
Turns out that creating a new function was easier than doing the TemplateType, so I did that first to test. And you can use a string. Here is an example that I just tested:

Code: Select all

<OnGlobalUniverseCreated>
<![CDATA[
   (typCreate 0xf0000001 (cat
      "<ItemType name='cool item' level='1' value='100' mass='1' frequency='notRandom' description='Wow, I just created this!'>"
      "<Image imageID='&rsItems1;' imageX='192' imageY='96' imageWidth='96' imageHeight='96'/>"
      "</ItemType>"
      ))
]]>
</OnGlobalUniverseCreated>
The CDATA syntax is just so that I can use angle-brackets in the string. And notice also that I used single-quotes instead of double-quotes for the attributes. I believe this is supported by standard XML and it makes it easier to embed in a string.

p.s.: I also called the function typCreate instead of unvCreateType.
User avatar
Atarlost
Fleet Admiral
Fleet Admiral
Posts: 2391
Joined: Tue Aug 26, 2008 12:02 am

Betel thinks unvtypcreate can't overwrite a static type the way templatetype can.

What I'm really after is nonrandom templatetype. A fully random weapon can get completely out of balance so I want to be able to eg. generate a damage string then generate a firerate subject to additional constraints based on the damage string then generate a power use dependent on the exact damage and firerate.

And then I want to be able to make another weapon dependent on the first. Suppose the first is my randomization of the Turbolaser. I want to be able to generate a dual turbolaser and omni turbolaser with stats based on the turbolaser.

To do this I need to either be able to overwrite static types with unvtypcreate or embed translisp in templatetype and control or at least predict the order of evaluation of templatetypes. (If they evaluate in UNID order I can do the actual randomization on whichever base type has the lowest UNID. If they evaluate in file order within a file I can put the simplest version first. Except possibly the Moskva line I think the simplest version always has the lowest UNID anyways.) I'm pretty sure if unvtypcreate is able to overwrite static types I can control the order by placing them in a block.
Literally is the new Figuratively
User avatar
Aury
Fleet Admiral
Fleet Admiral
Posts: 5421
Joined: Tue Feb 05, 2008 1:10 am
Location: Somewhere in the Frontier on a Hycrotan station, working on new ships.

Will we be able to change the stats once set? And if not, can we delete old ones so that we don't waste our UNID space?
(shpOrder gPlayership 'barrelRoll)
(plySetGenome gPlayer (list 'Varalyn 'nonBinary))
Homelab Servers: Xeon Silver 4110, 16GB | Via Quadcore C4650, 16GB | Athlon 200GE, 8GB | i7 7800X, 32GB | Threadripper 1950X, 32GB | Atom x5 8350, 4GB | Opteron 8174, 16GB | Xeon E5 2620 v3, 8GB | 2x Xeon Silver 4116, 96GB, 2x 1080ti | i7 8700, 32GB, 6500XT
Workstations & Render machines: Threadripper 3990X, 128GB, 6900XT | Threadripper 2990WX, 32GB, 1080ti | Xeon Platinum 8173M, 48GB, 1070ti | R9 3900X, 16GB, Vega64 | 2x E5 2430L v2, 24GB, 970 | R7 3700X, 32GB, A6000
Gaming Systems: R9 5950X, 32GB, 6700XT
Office Systems: Xeon 5318Y, 256GB, A4000
Misc Systems: R5 3500U, 20GB | R5 2400G, 16GB | i5 7640X, 16GB, Vega56 | E5 2620, 8GB, R5 260 | P4 1.8ghz, 0.75GB, Voodoo 5 5500 | Athlon 64 x2 4400+, 1.5GB, FX 5800 Ultra | Pentium D 3.2ghz, 4GB, 7600gt | Celeron g460, 8GB, 730gt | 2x Athlon FX 74, 8GB, 8800gts 512 | FX 9590, 16GB, R9 295x2 | E350, 8GB | Phenom X4 2.6ghz, 16GB, 8800gt | random core2 duo/atom/i5/i7 laptops
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

Atarlost wrote:Betel thinks unvtypcreate can't overwrite a static type the way templatetype can.

What I'm really after is nonrandom templatetype. A fully random weapon can get completely out of balance so I want to be able to eg. generate a damage string then generate a firerate subject to additional constraints based on the damage string then generate a power use dependent on the exact damage and firerate.

And then I want to be able to make another weapon dependent on the first. Suppose the first is my randomization of the Turbolaser. I want to be able to generate a dual turbolaser and omni turbolaser with stats based on the turbolaser.

To do this I need to either be able to overwrite static types with unvtypcreate or embed translisp in templatetype and control or at least predict the order of evaluation of templatetypes. (If they evaluate in UNID order I can do the actual randomization on whichever base type has the lowest UNID. If they evaluate in file order within a file I can put the simplest version first. Except possibly the Moskva line I think the simplest version always has the lowest UNID anyways.) I'm pretty sure if unvtypcreate is able to overwrite static types I can control the order by placing them in a block.
I think I get it.

TemplateTypes within an extension are guaranteed to evaluate in UNID order. [Eventually, I will probably have to alter the evaluation order of each extension based on dependencies. For example, if extension B depends on extension A, then TemplateTypes in extension A need to evaluate first, regardless of UNID.]

I should create an event that fires before any TemplateType is evaluated (something like <OnGlobalInitTypes>). You could create all your randomizations there in the proper order (and store them in global variables or whatever). Then all your TemplateTypes (for the turbolaser and dual turbolaser) can refer to the globals. Then you don't care what order the TemplateTypes get evaluated in.

It would also be pretty easy for me (I think) to allow OnGlobalInitTypes to call unvCreateType to override static types (this would be the only event in which you could do that). Then you can do all the work in script without any TemplateTypes.

p.s.: Now I know why you want this: http://wiki.neurohack.com/transcendence/trac/ticket/741 (which is unfortunately not that easy). If you could extract the XML from an existing type you could alter it and create a new type. For example, you could have a function that creates an omni version of any existing weapon.
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

Wolfy wrote:Will we be able to change the stats once set? And if not, can we delete old ones so that we don't waste our UNID space?
No, not within a game, unfortunately. [Nor can we delete them, since we would have to search through all systems looking for references to the type you deleted--or risk a crash.]

But don't worry about UNID space. You can call unvDynamicUND more than 250,000,000 times before it runs out of UNIDs (it allocates UNIDs out of the reserved 0xF??????? space). [And this is a per-game limit--if you start a new game, the limit resets.]
User avatar
alterecco
Fleet Officer
Fleet Officer
Posts: 1658
Joined: Wed Jan 14, 2009 3:08 am
Location: Previously enslaved by the Iocrym

<OnGlobalInitTypes> seems like a very good idea.

Also, do angle brackets inside strings really get interpreted as xml if not inside a CDATA block?
Get your own Galactic Omni Device
Get it now. It's free!!
Image
User avatar
Betelgeuse
Fleet Officer
Fleet Officer
Posts: 1920
Joined: Sun Mar 05, 2006 6:31 am

a few of functions that came from irc discussions (should I make tickets for each of these?)

(itmSetFrequency UNID frequency) ;sets the frequency of an item
(staSetLevelFrequency UNID levelFrequency) ;sets the level frequency of a station

this would allow us to remove old items and stations (both static and dynamic) without breaking the game

(shpSwapWeapon ship equippedItem itemToBeSwapped) -> the swapped out item ; you would use this to install the itemToBeSwapped preserving weapon selected

(shpSwapArmor ship equippedItem itemToBeSwapped) -> the swapped out item ; you would use this to install the itemToBeSwapped preserving hp without

(shpSwapShield ship equippedItem itemToBeSwapped) -> the swapped out item ; you would use this to install the itemToBeSwapped preserving hp
Crying is not a proper retort!
User avatar
Atarlost
Fleet Admiral
Fleet Admiral
Posts: 2391
Joined: Tue Aug 26, 2008 12:02 am

The swap functions exist already. There's a minor problem with the weapon swapping function that can be fixed with tickets49 and 748.
Literally is the new Figuratively
Post Reply