Modding:Interior Zones

From Caves of Qud Wiki
(Redirected from Modding:Vehicles)
Jump to navigation Jump to search
This article is a stub. You can help Caves of Qud Wiki by expanding it.
This article is a stub. You can help Caves of Qud Wiki by expanding it.

Interior zones are special zones that can be attached to objects and entered by using the object's enter action. They can be constructed purely using XML-based mods, and (with sufficient imagination) can simulate vehicles, buildings, and much, much more.

In-game, interior zones are typically used with the Vehicle part to simulate vehicles:

However, interiors are also used for entirely different purposes by mods:

  • Armithaig's Hearthpyre uses interiors to model the inside of tents, tipis, and yurts.
  • Mycogrigoric Alcoves (a purely-XML mod) uses interiors to simulate distant space outposts.

Creating an interior zone

You need the following components to create an interior zone in-game:

  • a map file (a .rpm created using the map editor) for the zone;
  • a cell for the zone in the Interior world defined in Worlds.xml;
  • an object that you want to attach the zone to; and
  • an object (typically some kind of door, hatch, portal, etc.) that you have placed inside the zone that you will use as an exit.

Creating vehicles with interior zones

You can use interior zones to create a wide variety of different vehicles using XML modding. To create a vehicle, you must typically create an interior zone using the process outlined in the previous section, and then

  • apply the Vehicle part (and any other applicable parts) to the same object that you applied the Interior part to;
  • add a driver's seat (or some other control mechanism) inside the vehicle interior;
  • (if applicable) add a VehicleEjectionSlot widget inside the vehicle underneath seats that the player should be able to eject from;
  • (if applicable) add an object with the InteriorContainer part that will allow you to access and trade with the vehicle's inventory.

Handling interior weights

A common problem when designing vehicles is that objects in the vehicle's interior zone -- including walls, furniture, and so on -- are included in the vehicle's overall weight. This can lead to situations in which the vehicle is carrying millions of pounds of weight before accounting for the player, their inventory, or any objects the player may put inside the vehicle[1].

There are a few different methods for solving this problem:

Option 1: annotate each object in the interior map file with the InteriorRequired property.

The way that the base game tends to ignore interior weights is to modify the map file so that each object in the vehicle's interior has the InteriorRequired int property. For example, here's a snippet from TempleMechaMkIIInterior.rpm (used by temple mecha mk Ia and temple mecha mk Ib):

<!-- snip -->
<cell X="0" Y="0">
  <object Name="MechInteriorWall">
    <intproperty Name="InteriorRequired" Value="1" />
  </object>
</cell>
<cell X="1" Y="0">
  <object Name="MechInteriorWall">
    <intproperty Name="InteriorRequired" Value="1" />
  </object>
</cell>
<cell X="2" Y="0">
  <object Name="MechInteriorWall">
    <intproperty Name="InteriorRequired" Value="1" />
  </object>
</cell>
<!-- snip -->

This adds the InteriorRequired property to the MechInteriorWall instances in the vehicle's interior. Objects with this property have their weight ignored when calculating the full weight of an interior.

The downside of this method is that there is not (at the time of writing) a simple way to add this property to every object in a map, outside of manually editing the .rpm file for it.

Option 2: create copies of objects and give them the InteriorRequired property.

Another option is to define near-copies of the blueprints for all of the objects that you want to put in your vehicle's interior, the only difference being that the new versions of the objects have the InteriorRequired property attached to them. Returning to the example of templar mechs, we could define a new object blueprint such as the following:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object Name="MechInteriorWall_InteriorRequired" Inherits="MechInteriorWall">
    <intproperty Name="InteriorRequired" Value="1" />
  </object>
</objects>

Then in our .rpm file, we would use the InteriorRequired version of the wall rather than the original one:

<!--
  Modified version of TemplarMechaMkIIInterior.rpm that uses the InteriorRequired version of the MechInterior wall rather
  than the original.
-->
<!-- snip -->
<cell X="0" Y="0">
  <object Name="MechInteriorWall_InteriorRequired" />
</cell>
<cell X="1" Y="0">
  <object Name="MechInteriorWall_InteriorRequired" />
</cell>
<cell X="2" Y="0">
  <object Name="MechInteriorWall_InteriorRequired" />
</cell>
<!-- snip -->

This approach is much easier to work with from the map editor. On the other hand, it requires quite a lot of up-front work to create copies of every object you might plausibly want in the interior of your zone, especially if you are designing a complex vehicle interior with many objects.

Option 3: set the IgnoreWeight flag.

In version 207.76, a new IgnoreWeight was added to the Interior part that allows vehicles to ignore the weight of objects inside of them. Setting it is a matter of modifying the Interior part on your vehicle, e.g.:

<!-- The following example is taken from the VehicleTemplarMech blueprint -->
<!-- snip -->
<part Name="Interior" Cell="TempleMechaMkI" FallDistance="1" IgnoreWeight="true" />
<!-- snip -->

This cause your vehicle to completely ignore the weight of any objects inside of it. This method has the benefit of being relatively simple. However, it contradicts standard game behavior, in that you can put as many things into your vehicle as you want without it going over carry capacity, which you may not necessarily find desirable.

Example: Templar Mechs

The temple mecha mk Ia and temple mecha mk Ib are two late-game creatures that are implemented as vehicles using interior zones. In this section, we'll explore how these creatures are implemented in the game's code.

First, we'll start by looking at their respective blueprints. Both mechas inherit from the base VehicleTemplarMech object, defined in ObjectBlueprints/Creatures.xml:

<object Name="VehicleTemplarMech" Inherits="BaseRobot">
  <part Name="Body" Anatomy="BipedalRobot" />
  <part Name="Render" ColorString="&amp;c" TileColor="&amp;c" DetailColor="r" />
  <part Name="Brain" Factions="Inanimate-100"  />
  <!-- since the vehicle is not autonomous and shuts down without a driver, make it neutral unpiloted, uses driver faction when piloted as usual -->
  <part Name="CannotBeInfluenced" Messages="Beguiling::The place where a mind should be is blank and smooth in =subject.t=.;;Persuasion_Proselytize::=subject.T's= commitment to =pronouns.possessive= cause is unwavering.;;LoveTonicApplicator::The tonic has no effect on =subject.t=.;;default::=subject.T= =verb:are= insensible to your blandishments." />
  <stat Name="Level" Value="40" />
  <stat Name="AV" Value="15" />
  <stat Name="HeatResistance" Value="50" />
  <stat Name="ColdResistance" Value="50" />
  <stat Name="Hitpoints" Value="900" />
  <skill Name="Acrobatics_Jump" />
  <!--
    truncated for brevity; you can find the rest of the definition in the
    game's data files

    ...
  -->
</object>

Most of this definition pertains to various skills, stats, abilities, and inventory objects that the mechs have. For our purposes, here is the crucial bit that makes the mech work as a vehicle:

<object Name="VehicleTemplarMech" Inherits="BaseRobot">
  <!-- ... -->
  <part Name="Interior" Cell="TempleMechaMkI" FallDistance="1" />
  <part Name="Vehicle" ChargeMinimum="1000" Type="TemplarMech" Autonomous="false" IsEMPSensitive="true" IsTechScannable="true" BindBlueprint="Purple Security Card" />
  <part Name="VehiclePilotPopulation" Blueprint="Templar Squire,Gunner-Knight Templar" />
  <part Name="VehicleMeleeInfiltration" />
  <part Name="VehicleSocketSeal" />
  <!-- ... -->
</object>

Going through these parts one-by-one:

  • Interior: this part gives the mecha an interior zone, using the TempleMechaMkI cell from Worlds.xml (which we'll examine in a moment). The FallDistance="1" attribute causes creatures inside the mech to fall (and thus take fall damage) if the mech is destroyed.
  • Vehicle: this part is what makes the mech a vehicle. Most of this part's attributes are inherited from IActivePart; see Modding:Active Parts for more details. This part also accepts a couple of other attributes. Type is a string that is inserted in the vehicle's VehicleRecord, which is used primarily by the reshaping nook to repair and recall the player's golem (although this system could be hijacked by modders for use by other vehicles). BindBlueprint is used by the VehicleSeat part (described later in this example) to determine whether or not the player can pilot the vehicle. Another notable attribute not used in this example is Autonomous. When set to true, this allows the vehicle to operate without a pilot.
  • VehiclePilotPopulation: this defines the object blueprint for the creature that should pilot the mech by default. Note that Table="..." can be specified instead of Blueprint to have the pilot spawn from a population table.
  • VehicleMeleeInfiltration: allows players to infiltrate the vehicle. See VehicleMeleeInfiltration.cs for more information[2].
  • VehicleSocketSeal: prevents the player from replacing the mech's power cell unless they own the vehicle and it is currently unpiloted.

Next, let's take a look at how the TempleMechaMkI cell is defined in Worlds.xml:

<worlds>
  <!-- ... -->
  <world Name="Interior" ZoneFactory="InteriorWorldZoneFactory" ZoneFactoryRegex="^Interior" DisplayName="Inside" Plane="Inherit" Protocol="Inherit">
    <!-- ... -->
    <cell Name="TempleMechaMkI">
      <boolproperty Name="JoinPartyLeaderPossible" Value="false" />
      <zone Level="10" x="1" y="1" Name="Control pit" NameContext="Temple mecha mk I" AmbientBed="sfx_endgame_golem_int_lp" IncludeStratumInZoneDisplay="false">
        <builder Class="InteriorGround" />
        <builder Class="MapBuilder" FileName="TempleMechaMkIInterior.rpm" Width="5" Height="3" ClearBeforePlace="true" />
        <widget Blueprint="AmbientLight" />
        <intproperty Name="AmbushChance" Value="10" />
      </zone>
    </cell>
    <!-- ... -->
  </world>
</worlds>

You can find more information on how to interpret this in Modding:Worlds. The most important part of this is the MapBuilder zone builder, which tells the game to use the TempleMechaMkIInterior.rpm map file. It also tells the game that the expected dimensions of this map are 5 tiles wide by 3 tiles tall.

TempleMechaMkIInterior.rpm exists in the game's data files. You can find it and open it in the map editor:

Temple Mecha Mk I Interior - Map Editor.webp

Notice that the entire map is located in the upper-left corner. Interior maps must start from this tile, even if their dimensions are less than the normal 80 wide by 25 tall used by most zones in the game.

Despite its small size, this zone contains a few important components. First, it has an exit, in the form of the MechExitHatch object:

<!-- ... -->
<cell X="1" Y="1">
  <object Name="MechExitHatch">
    <intproperty Name="InteriorRequired" Value="1" />
  </object>
</cell>
<!-- ... -->

MechExitHatch inherits from VehicleGolemExit, which in turn has the InteriorPortal part. This is the part that allows exit from the vehicle.

The mech also has a control mechanism via its MechPilotSeat object, as well as an ejection slot:

<!-- ... -->
<cell X="3" Y="1">
  <object Name="VehicleEjectionSlot"></object>
  <object Name="MarbleFloor"></object>
  <object Name="MechPilotSeat">
    <intproperty Name="InteriorRequired" Value="2" />
  </object>
</cell>
<!-- ... -->

The MechPilotSeat is the object that allows you to actually control the mech via the VehicleSeat part. This object also has an EjectionSeat part, which allows you to eject from the mech. Note that this seat must be placed on a cell that also has the VehicleEjectionSlot widget in order for ejection to be possible[3][4].

A final important component that doesn't appear in this vehicle but can be found in others (notable, the golem and temple mecha mk II) is the presence of a "container" object. For example, the temple mecha mk II (which uses the TempleMechaMkIIInterior.rpm map file) has a MechInteriorContainer in its interior:

<!-- ... -->
<cell X="5" Y="1">
  <object Name="MechInteriorContainer">
    <intproperty Name="InteriorRequired" Value="0" />
  </object>
</cell>
<!-- ... -->

This container gives the mech an innate inventory that it can use to store objects and trade with other creatures via the InteriorContainer part[5].

Limitations of interiors

As of patch 207.82, multi-zone interiors are not supported by the game. If you wish to construct something similar to a multi-zone interior, you will need to mod in your own world.

Note that it is possible to nest interior zones inside of one another. For example, suppose you wanted to simulate a spaceship consisting of several rooms. You could start by creating a Spaceship object with an Interior part that allows you to enter the ship's control room. To enter the ship's engine room, you could have an Engine Room Door object that leads into the zone for the engine room, and so on.

Footnotes

  1. This is usually only a problem when designing interiors for vehicles. For general interior zones you do not usually need to care about weight.
  2. XRL.World.Parts.VehicleMeleeInfiltration
  3. XRL.World.Parts.EjectionSeat
  4. XRL.World.Parts.EjectionSlot
  5. XRL.World.Parts.InteriorContainer