How to handle a parallel inheritance?

Fri Feb 04, 2022 10:36 am

I want to create my own object to extend the actor class. Let's call it UW_ObjectBase because my actual object will then inherit from that.
Then I want some of these objects be pickups, so I'll create a class called UW_PickupBase for those items to inherit. The thing is I want that class to inherit Inventory for it's features but also UW_ObjectBase for it's features.
Then I want to create a UW_WeaponBase that will inherit Weapon but also needs to inherit the other UW features.

What's the best way to handle this?

Also, I see this on the Classes:Weapon wiki page:
A player can never carry more than one weapon of each kind.

Like with normal inventory items, an actor can have more than one sample of a weapon in its inventory.

What does that mean, only one kind but more than one sample? If I had two of the same weapon definitions, say an axe, but one had a durability of 5 and the other 20, and I want to carry both, then 5 until it breaks and then I'll switch to the 20. How would I code that?

Re: How to handle a parallel inheritance?

Fri Feb 04, 2022 11:14 am

Option one: make UW_PickupBase inherit UW_ObjectBase.
Option two: use mixins. Basically, a mixin is a piece of code that you can mix in into other classes.

Not sure about the second question.

Re: How to handle a parallel inheritance?

Fri Feb 04, 2022 12:19 pm

A player can never carry more than one weapon of each kind.

This is blatantly false or at least it used to be, I don't know if it still is. It at least used to be possible to set a weapon's Inventory.MaxAmount to something greater than 1 and be able to actually have multiple copies of the same weapon in your inventory. I used this in some of my mods before ZScript was even a thing in order to allow sharing weapons in Cooperative games.

EDIT:
Rereading it, I think it is just a poorly worded way of saying while you can multiple copies of a weapon in your inventory, you can only ever select 1 copy (probably the first you picked up) to use. Thus if you had multiple copies but they had different attributes, you would always be using the same copy with the same attributes no matter what.

Re: How to handle a parallel inheritance?

Fri Feb 04, 2022 12:28 pm

Note that a mixin is basically a copy-paste instruction for the compiler, it just saves you the step of doing it yourself and having duplicated code. Just be aware that the resulting classes, even though they may have identically-named methods and members, would not be part of the same inheritance chain.

You can either do this (same inheritance chain)
Code:
Actor → UW_ObjectBase → UW_PickupBase
         FunctionA    →  FunctionA
         FunctionB    →  FunctionB
                         FunctionC

Or this (separate inheritance chains)
Code:
Actor → UW_ObjectBase
         MixIn
          FunctionA
          FunctionB

Actor → UW_PickupBase
         MixIn
          FunctionA
          FunctionB
         FunctionC

TL;DR ZScript has no "parallel" or "shared" inheritance functionality.

8-)

Re: How to handle a parallel inheritance?

Fri Feb 04, 2022 1:44 pm

I think what this means (and tell me if I'm wrong, I'm no professional programmer, just some guy trying to figure this stuff out) is that what I need to do is:
Code:
Actor → UW_ObjectBase
Inventory → UW_PickupBase
Weapon → UW_WeaponBase

Then all of these will return true for is "actor" but not for is UW_ObjectBase
So even if I add the same function to each of them, say GetDescription(), I won't be able to call it universally like this:
Code:
string Description=UW_ObjectBase(ThisObject).GetDescription()

But I'd have to do it the long way, like this:
Code:
string Description
if (ThisObject is "UW_ObjectBase"){Description=UW_ObjectBase(ThisObject).GetDescription()}
if (ThisObject is "UW_PickupBase"){Description=UW_PickupBase(ThisObject).GetDescription()}
if (ThisObject is "UW_WeaponBase"){Description=UW_WeaponBase(ThisObject).GetDescription()}

Am I understanding that correctly? Basically I'd need to add that "translation layer" every time I want to get a UW property or function off of an object.
I suppose I could build a class with static functions like string GetDescription(Actor ThisObject) and use that to pick up data off the UW objects so I don't have to clutter up my code with all those ifs.

Is that the way to do this or is there something better/easier?

Re: How to handle a parallel inheritance?

Fri Feb 04, 2022 3:04 pm

On the face of it, that seems to be the case, yes. You'd either need to cast the intended type, or use a static function with a translation later.

8-)

Re: How to handle a parallel inheritance?

Thu Feb 10, 2022 7:25 pm

OK, I got it, I can work around that then. Another question - How do I make one mod aware of another?

Say I've got mod1 that defines "class NewActor : Actor"

then in mod2 I've got code like this:
Code:
void MyFunc(Actor a){
   if (a is "NewActor"){
      let n=NewActor(a);
      //work with n
   } else {
      //work with a
   }
}

If mod1 isn't loaded and that class isn't defined, then this script won't even compile. So how to I make code so that it can use a class only if it is defined?

Re: How to handle a parallel inheritance?

Fri Feb 11, 2022 10:27 am

One method which seems to be allowed is putting the classname in a variable so it's evaluated at run-time instead of compile-time. See this wiki page: ZScript_classes: Cross-Mod_Support
Code:
// Checked at compile time. This will error upon loading if the class is missing.

class<Actor> cls = "MissingNameOfClass";
Code:
// Checked at run time instead of compile time. 'cls' will just be null if the actor class isn't defined.

string classname = "MissingNameOfClass";
class<Actor> cls = classname;
I personally don't like this because I feel like it's a workaround or hack, rather than an intentional feature. That said, it works, and is fairly simple to understand and implement.

The other way is using the Service system. Essentially this is like a "bulletin board" where one mod can put out a notice with some information, and another mod can retrieve that information without necessarily needing to be aware of how it got there. I have not used this myself (though I did implement a simple version in my own mod at one point, before this feature was added) and it seems kind of complicated to set up, but is probably the most flexible and future-proof option. Plus it's an intentional feature, not a workaround.

8-)

Re: How to handle a parallel inheritance?

Fri Feb 11, 2022 5:21 pm

Caligari87 wrote:One method which seems to be allowed is putting the classname in a variable so it's evaluated at run-time instead of compile-time. See this wiki page: ZScript_classes: Cross-Mod_Support
[...]
I personally don't like this because I feel like it's a workaround or hack, rather than an intentional feature. That said, it works, and is fairly simple to understand and implement.

I think it's safe not to consider it a hack. It is intentional, and it's only a workaround here because it doesn't let you do it directly.
I believe the reason why it errors out is for user friendliness and so mistakes are easier to spot... But it gets in the way when you do mean to do that.