Tiles
Tiles are physically based around the 1m. There are various tile sizes depending on the use.
2m environmental tiles
Note that the following section are for explains 2m environmental (env) tiles which are measured at 2x2m. This is different from other kinds of sprites such as props, NPC, or player sprites, which take up smaller space.
Nominal floor height and camera pitch
Nominal floor height is 2m. Because of this the camera pitch angle of 35.2643°.
The nominal floor height refers to the physical height required to align the next level to the isometric grid from a specific camera angle.
A decision was made to pre-define the floor height, and then work out the camera angle. This was for the benefit of having a round value for a working physical space (eg 2m, and not 2.12m)
Tile ratio
At camera pitch 35.2643°, the tile w/h ratio is sin(35.2643°), which is 0.5773.
Tile isometric resolution
Note that the isometric resolution refers the tile’s pixel dimensions in isometric view. Tile resolution can be variable, but the nominal iso width resolution for a 2m physical env tile is 256 pixels. 128 pixels.
A 1m prop tile, however, is 128 pixels. 64 pixels.
The floor-only env tile resolution, thus, is 256*tileRatio, which is 147.8 pixels, rounded off as 148 pixels in height.
The floor only env tile resolution thus, is 128px*tileRatio == 74 (height).
Thus the floor-only env tile iso resolution is 256w x 148h px. 128w x 74h px.
The 1m prop rile iso reolution, on the other hand, is 128w x 74h px. 64w x 37h px.
The reason why it is only floor-only is because the actual tile sprite can be much taller. The nominal floor-only is simply the basis for establishing scale.
Tile sprite resolution
Tiles have no fixed resolution, but can have double/triple/half the nominal width resolution depending on requirements.
The resolution of the sprite’s must always be accurately divisible by 148 74 pixels, which is the height of the nominal iso floor tile.
The image below shows that the height resolution is 3x the height of the sprite.
The height resolution can be solved by h=
.148 74*num
Here is a sample list of some widths for both 2×2 tiles:
64 x 37 96 x 55.5 (high fractional value) 128 x 74 192 x 111 256 x 148 384 x 221.7 (high fractional value) 480 x 277 512 x 295.6 (high fractional value) 768 x 443 1024 x 591 2048 x 1182
Here is one for height for both 2×2 tiles:
The width resolution can be solved by w=128*tileNum
, where tileNum is the number of tiles per side. Ie the above image 2×2, so w=128*2
.
Orthographic camera settings (LW)
In LW, the orthographic camera is set with a physical distance parameter which is the value used to determine how much physical space will fit the pixel dimensions.
Use the Pythagorean theorem, c=sqrt(a^2+b^2)
to determine the length of the hypotenuse from one corner of the tile to the other.
If we were getting a 1×1 tile, then the computed value would be 1.4142
. However, we are using 2×2 tile configuration as shown in the image above, so we double the value. The Size parameter is set to ‘Horizontal Size’, and the value is set to 2.8284
for the hypotenuse value of a 2×2 sprite configuration.
The following is a LW scene which sets up an isometric camera based on user-defined settings.
LWSC 5 RenderRangeType 0 FirstFrame 1 LastFrame 120 FrameStep 1 RenderRangeObject 0 RenderRangeArbitrary 1-120 PreviewFirstFrame 0 PreviewLastFrame 120 PreviewFrameStep 1 CurrentFrame 0 FramesPerSecond 24 ChangeScene 0 Plugin MasterHandler 1 .SceneEditorStandardBanks EndPlugin Plugin MasterHandler 2 SceneEditor VERS 2 { CurrentWorkspace Name "<default>" WindowDimensions 1438 24 1de 430 Divider1 0.30000001 LayoutItemFilterEnabled 0 LayoutItemChannelFilterEnabled 0 SurfaceFilterEnabled 0 SurfaceChannelFilterEnabled 0 ChannelFilterEnabled 0 { Bank PresetID 5f434856 Column 5f434856 0 90 Column 5f434856 1 90 Column 5f434856 2 112 Column 5f434856 3 95 Column 5f434856 4 106 } { SurfaceListBank PresetID 5f434856 Column 5f434856 0 90 Column 5f434856 1 90 Column 5f434856 2 112 Column 5f434856 3 95 Column 5f434856 4 106 } { ChannelListBank PresetID 5f434856 Column 5f434856 0 90 Column 5f434856 1 90 Column 5f434856 2 112 Column 5f434856 3 95 Column 5f434856 4 106 } BackgroundColorOdd 78 78 78 ff BackgroundColorEven 6f 6f 6f ff HierarchyIndent c HighlightColor b5 b0 a5 ff DefaultObjectColor b0 b0 b0 ff DefaultBoneColor 60 e0 f0 ff DefaultLightColor f0 60 f0 ff DefaultCameraColor 20 e0 20 ff AutoApplyToViewPorts 1 MultiselectSimilarTypes 1 LimitColorRange 1 ColorsAsHSV 0 ShowSelectionOrder 1 EnvelopeButtonOpensGraphEditor 1 ShowRenderExtents 1 BlockPeriod 0.041666668 BlockWidthMin 3 BlockWidthMax c8 ShowKeyTicks 1 OverrideBlockColors 0 BlockKeyColor 99 ae cf ff BlockGroupColor 64 85 b6 ff DSAllowChannelGroupSelection 1 } EndPlugin Plugin ViewportObjectHandler 1 .VPR 0 20 1 1 0 EndPlugin Plugin ViewportObjectHandler 2 .VPR 0 20 1 1 0 EndPlugin Plugin ViewportObjectHandler 3 .VPR 0 20 1 1 0 EndPlugin Plugin ViewportObjectHandler 4 .VPR 0 20 1 1 0 EndPlugin ColorSpaceViewer "sRGB" "none" ColorSpaceSurfaceColor "sRGB" "none" ColorSpaceLightColor "sRGB" "none" ColorSpacePaletteFiles "sRGB" "none" ColorSpace8BitFiles "sRGB" "none" ColorSpaceFloatFiles "Linear" "none" ColorSpaceAlpha "Linear" "none" ColorSpaceOutput "sRGB" "none" ColorSpaceOutputAlpha "Linear" "none" ColorSpaceAutoSense 1 ColorSpace8BitToFloat 1 ColorSpaceCorrectOpenGL 1 ColorSpaceAffectPicker 1 FirstBackgroundImageSyncFrame 0 NavigationDesiredDevice (none) AddNullObject 10000000 orient ChangeObject 0 ShowObject 7 3 Group 0 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0.52359879016876221 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "z" } } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 LockedChannels 40 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000001 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AddNullObject 10000001 heading ChangeObject 0 ShowObject 7 4 Group 0 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0.78539818525314331 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000005 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AddNullObject 10000002 tilesize ChangeObject 0 // Use 'sz' to define the nominal sprite resolution for 1m for both x and y // Set the camera to the sx/sy shown in the slider bank. // Use px to define the nominal tile physical size (pz follows px) ShowObject 7 0 Group 0 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 2 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "follow_tilesize_px" } } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 256 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "tilesize_sx" } } Channel 7 { Envelope 1 Key 256 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "tilesize_sy" } } Channel 8 { Envelope 1 Key 128 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000004 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } Plugin CustomObjHandler 1 Sliders 2 6 5 80 -483 348 0 0 0 0 0 0 "tilesize" "Position.X" 10000002 0 16 0.1254902 0.87843138 0.1254902 16 "nominal tile physical size (m)" "tilesize" "Scale.X" 10000002 0 1 0.94117647 0.94117647 0.94117647 16 "Set frame res X" "tilesize" "Scale.Y" 10000002 0 1 0.94117647 0.75294119 0.1254902 16 "Set frame res Y" "tilesize" "Scale.Z" 10000002 16 2048 0.94117647 0.3764706 0.94117647 16 "nominal tile resolution @ 1m" "floortile" "Scale.X" 10000003 16 2048 0.94117647 0.1254902 0.1254902 16 "floortile X" "floortile" "Scale.Y" 10000003 16 2048 0.3764706 0.87843138 0.94117647 19 "floortile Y" EndPlugin Plugin CustomObjHandler 2 LW_ItemComment 2 10000002 1 0.2 0.92000002 0.2 1 14 EndPlugin NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AddNullObject 10000003 floortile ChangeObject 0 // Use sx/sy to define floor tile pixel targets. ShowObject 7 0 Group 0 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 256 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 128 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000004 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } Plugin CustomObjHandler 1 LW_ItemComment 2 10000003 1 0.2 0.92000002 0.2 1 14 EndPlugin NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AddNullObject 10000004 _do_not_touch_ ChangeObject 0 ShowObject 7 0 Group 0 ItemLock 1 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000005 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AddNullObject 10000005 iso_cam_setup ChangeObject 0 ShowObject 7 4 Group 0 ObjectMotion NumChannels 9 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 SubPatchLevel 3 3 APSDisplay { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } APSRender { APS Version 1 Method 0 { VParm { ObjectLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonLevel 0 { VariantParameter 3 0 { ParameterValue 3 0 } } } { PolygonPixelSize 0 { VariantParameter 3 0 { ParameterValue 256 0 } } } } } NodeDisplacement 0 { Nodal_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Displacement" { Tag RealName "Displacement" Name "Displacement" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } Server "Input" { Tag RealName "Input" Name "Input" Coordinates -30 -10 Mode 1 { Data } Preview "Object Position" Comment "" } } { Connections } } NodeDisplacementOrder 0 UseObjGI 0 ObjGIRadiosityRays 64 ObjGISecondaryBounceRays 16 ObjGIRadiosityTolerance 0.292893 ObjGIMinPixelSpacing 4.000000 ObjGIMaxPixelSpacing 100.000000 ShadowOptions 7 NodeEdges 0 { NodalEdge_Block { Root Location 0 0 Zoom 1 Disabled 1 } Version 1 { Nodes Server "Edge" { Tag RealName "Edge" Name "Edge" Coordinates -10 -10 Mode 1 { Data } Preview "" Comment "" } } { Connections } } AmbientColor 1 1 1 AmbientIntensity 0.05 DoubleSidedAreaLights 1 RadiosityType 1 RadiosityInterpolated 1 RadiosityTransparency 0 RadiosityIntensity 1 RadiosityTolerance 0.2928932 RadiosityRays 100 SecondaryBounceRays 50 RadiosityMinPixelSpacing 3 RadiosityMaxPixelSpacing 100 RadiosityMultiplier 1 RadiosityDirectionalRays 0 RadiosityUseGradients 0 RadiosityUseBehindTest 1 BlurRadiosity 1 RadiosityFlags 0 RadiosityCacheModulus 1 RadiositySaveEachFrame 0 RadiosityCacheFilePath M:PROJECTS/___ARCHIVES/ASTRONETTE/ASTRONETTE/3d/Radiosity/radiosity.cache PixelFilterForceMT 0 AddLight 20000000 LightName Light ShowLight 1 -1 0.941176 0.376471 0.941176 LightMotion NumChannels 9 Channel 0 { Envelope 1 Key -2 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 2 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 2 { Envelope 1 Key -2 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0.78539816339744828 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0.6108652381980153 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 6 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 7 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 8 { Envelope 1 Key 1 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 LightColor 1 1 1 LightIntensity 1 AffectCaustics 1 LightType 0 LensFlare 0 FlareIntensity 0.5 FlareDissolve 0 LensFlareFade 4 LensFlareOptions 11 FlareRingColor 0.3137 0.0784 0.0392 FlareRingSize 0.22 FlareRandStreakInt 0.03 FlareRandStreakDens 50 FlareRandStreakSharp 6 ShadowType 1 ShadowColor 0 0 0 Plugin LightHandler 1 DistantLight EndPlugin AddCamera 30000000 CameraName Camera ShowCamera 1 -1 0.125490 0.878431 0.125490 CameraMotion NumChannels 6 Channel 0 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 1 { Envelope 1 Key 0.70710700750350952 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "camera_height" } } Channel 2 { Envelope 1 Key -10 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 3 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 4 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } Channel 5 { Envelope 1 Key 0 0 0 0 0 0 0 0 0 Behaviors 1 1 } IKInitCustomFrame 0 GoalStrength 1 IKFKBlending 0 IKSoftMin 0.25 IKSoftMax 0.75 CtrlPosItemBlend 1 CtrlRotItemBlend 1 CtrlScaleItemBlend 1 LockedChannels 5 PathAlignLookAhead 0.033 PathAlignMaxLookSteps 10 PathAlignReliableDist 0.001 ParentItem 10000000 SplineFlags 0 SplineFit 0 IKInitialState 0 UseIKChainValues 1 ZoomFactor 3.2 ZoomType 1 ResolutionMultiplier 1.0 FrameSize 256 256 PixelAspect 1 MotionBlur 0 MotionBlurPasses 1 ShutterEfficiency 1 RollingShutter 0 ShutterOpen 0 Oversampling 0 UndockPreview 0 FieldRendering 0 ApertureHeight 0.015 EyeSeparation 0.07 ConvergencePoint 1 UseConvergence 0 StereoTrackedEye 0 StereoOpenGL 1 ConvergenceToeIn 1 DepthOfField 0 FocalDistance 1 LensFStop 4 DiaphragmSides 0 DiaphragmRotation 0 AASamples 1 Sampler 0 AdaptiveThreshold 0.01 MinimumSamples 1 MaximumSamples 9 Plugin CameraHandler 1 Orthographic { ACT Version 1 { VParm { Size 0 { VariantParameter 3 0 { ParameterValue 1.4142135 1 { Envelope 1 Key 1.4141999999999999 0 0 0 0 0 0 0 0 Behaviors 1 1 { ChannelHandler ".EnvExprLink" 0 1 "tilesize" } } } } } } } EndPlugin Antialiasing 2 AntiAliasingLevel -1 ReconstructionFilter 0 AdaptiveSampling 1 UseBackgroundColor 0 BackgroundColor 0 0 0 SolidBackdrop 1 BackdropColor 0 0 0 ZenithColor 0.6196 0.736 1 SkyColor 0.8824 0.9177 1 GroundColor 0.8824 0.9177 1 NadirColor 0.1216 0.0948 0.083 SkySqueezeAmount 6 GroundSqueezeAmount 6 FogType 0 FogMinDistance 0 FogMaxDistance 1 FogMinAmount 0 FogMaxAmount 1 FogColor 0.5098 0.5098 0.549 BackdropFog 0 CopyToPerspective 0 VolumeClipDiscance 0.1 DynamicRangeMin 0 DynamicRangeLimit 1 DitherIntensity 0 AnimatedDither 0 RenderMode 2 RayTraceEffects 15 DepthBufferAA 0 RenderLines 1 RenderInstances 1 RayRecursionLimit 6 RayPrecision 6 RayCutoff 0.01 ShadingSamples 1 LightSamples 1 DataOverlayLabel SaveRGB 0 SaveAlpha 0 ViewConfiguration 0 DefineView 0 ViewType 9 ViewLevel 5 ViewAimpoint 0 0 0 ViewZoomFactor 1.312894 ViewXRay 1 ViewMBDofPreview 0 ViewHeadlight 0 GridNumber 10 GridSize 1 CameraViewBG 0 ShowMotionPath 1 ShowFogRadius 1 ShowFogEffect 0 ShowFieldChart 0 OverlayColor_fv 0 0 0 MeshBackgroundGroup 1 Plugin VirtualStudio 1 VirtualStudio { Version 2 Active 1 LIVE 0 LIVE_FPS 30 Play 0 Arm 0 PunchIn 0 PunchOut 0 } EndPlugin CurrentObject 5 CurrentLight 0 CurrentCamera 0 GraphEditorData { GraphEd_Favorites } { GE_Expression_Lib 1 Expression "Expr 2" "" 0 Expression "tilesize" "vmag( tilesize.wpos(Time))" 0 Expression "camera_height" "[Camera.Size]/2" 0 Expression "Expr 5" "Value" 0 Expression "tilesize_sx" "tilesize.pos(Time).x*tilesize.scl(Time).z" 0 Expression "tilesize_sy" "tilesize.pos(Time).z*tilesize.scl(Time).z" 0 Expression "z" "deg( asin(floortile.scl(Time).y/floortile.scl(Time).x) )" 0 Expression "follow_tilesize_px" "[tilesize.Position.X]" 0 } ImageEditorData NumberOfClips 0
Hacking (Useitemables)
Hacking is like using at item on an object, but requires a Haxbox, so this is the constant thing. The hackable object will then be tagged as hacked if successful.
Larger-than-tile-size images
Placing Tiles which are bigger than the Map’s tile size. A reference in the Tiled forums.
Summary of the issue is that multi-selecting tiles in while in Isometric projection does not yield a ‘flat’ orthographic placement of the tile, obviously. Ideally, a multi-selected Tile should be presented as a flat screen-space graphic, but the proper procedure, as Bjorn pointed out in the link above is to use a Collection of Images, so as to obviate the need to select multiple tiles to get to a bigger image.
This is fine in respect to the OP’s problem of putting in establishments. But my issues are putting walls, and all sorts of tiles that need to be connected precisely unto other tiles. The problem is how tiles are aligned in the first place. Tiles are anchored at the lower-left point. That means the bigger the tile gets in relation to the map tile size, the further up and right the tile’s registration will be.
On the outer hand, an Image Object aligns itself with the bottom anchor point making it ideal as a positioning entity.
I went ahead and modified TMX Importer V2 to retrieve the tileset id or image used by the Image Object, but Rex has also modified it after requesting it, so either way this is a doable solution.
Example requirements
- On LMB on NPC, walk and talk to NPC.
- On LMB on InvItem, walk and take InvItem.
- On LMB on Door, walk to Door and enter Room.
- Series of animation played end-to-end. (Use FSM?) (Determining end of animation)
Rex’s Scenario plugins
The Scenario plugin and its Behaviour counterpart need to be looked at in more detail.
Doors example
In the case of Doors, the PC can move onto the same Tile where Doors lie on, because Doors are rendered at the edges of the Tiles rather than at the centre of the tile (where the PC cannot be feasible rendered on top of it).
Portals exist for Doors; Portals are in the same position as the Door Sprite they belong to. When a Door Sprite is clicked, the PC Pathfinds to the Tile.
InvItem example
An InvItem, like a Door, exists on a Tile that the PC can move onto. Thus, the Tile is associated with InvItem directly.
NPC example
For NPCs, the PC cannot move on to the same Tile. If an NPC is clicked, this doesn’t automatically intend to Talk, but could be a move to the NPC position. However, the PC cannot move onto the same Tile as the NPC. Furthermore, the PC may not be able to reach a Tile that is adjacent to the NPC.
If PC intends to talk, the PC Pathfinds to the NPC.
Creating NPCs and NPC TalkPoints
IN TILED
NPCs
Create a Tiled Object type ‘npc’. I’m choosing to use Objects instead of a Tile in Tiled is because using a Tile will require a separate Tile Layer. In the processing of the TMX, it seems more clean/straightforward to process the Objects separately, so that the placement of the NPCs can occur on the Main layer (where Z-Sorting occurs).
The NPC will have the property called autoTP.
NPC.autoTP=-1 # -1 means NPC cannot be talked to NPC.autoTP=0 # 0 means manually placed TalkPoint is expected by system NPC.autoTP=1 # 1 means system will automatically generate TalkPoints based on accessibility of neighbouring Tiles
It will also have a ‘name’ property which denotes a unique string name for the NPC.
NPC.name='Letigus'
TalkPoints
If NPC.autoTP is = 0, then a TalkPoint in Tiled is expected to be manually placed. Create a Tiled Object type ‘talkpoint’. The name can be anything (as the property is read, not the name). Eg ‘tp1’.
A TalkPoint has a property called ‘npc’, which refers to the NPC.
TalkPoint.npc='Letigus' # specified in Tiled as this associated must be made during design # 'Letigus', obviously, as an NPC Object, must exist in the Tiled level as well
IN C2
Prep NPCs
C2 NPCs are composed of a generic ‘mover’ entity (NPCMover), and a unique ‘graphic’ entity (NPCSprite) that belongs to a Family (NPCSpriteFamily), both of which are Sprite C2 Objects.
/NPCMover ./NPCLegtigusSprite -> NPCSpriteFamily
NPCMover has a ‘name’ variable for the name of the NPC it is associated with.
NPCMover.name='Letigus' # this is populated when TMX is loaded NPCMover.sprite=-1 # this is NPCSpriteFamily.UID when it is generated
NPCSpriteFamily will have ‘name’ variable denoting the NPC it refers to; a ‘talkpoint’ variable pointing to to the UID of the Tile which is on the TalkPoint when/if it is made.
NPCSpriteFamily.f_name='Letigus' # Family variable init manually based on the Sprite's graphic NPCSpriteFamily.f_talkpoint=-1 # represents a Tile UID, valid only if .autoTP is 0 (manually placed TalkPoints) NPCSpriteFamily.f_autoTP=-1 # refer to above for value meanings NPCSpriteFamily.f_mover=-1 # this is the association to a particular mover, which is the positional marker
Prep TalkPoints
TalkPoints in C2 are expressed within Tiles. Tiles in the 0-layer are tagged with TalkPoint names, but init with blank strings (”)
Tile.talkpoint='Letigus' # when PC touches this Tile and queries it for Talking, it will return 'Letigus'. Tile.talkpoint=''
So a TalkPoint is not really a separated entity as such in C2, but rather, it is embedded in the Tiles since those are the ones being queried.
TMX Loading
This is the procedure during C2 TMX loading:
-Scan for Tiled Objects with type 'npc' -Query 'name' of 'npc' -Query 'autoTP' of 'npc'; for now assume .autoTP=0 -Query logical position of object -Look for 'name' in NPCSpriteFamily.f_name, assume found -Instantiate NPCMover on Main layer, and assign NPCMover.name = NPCSpriteFamily.f_name -Assign NPCMover.sprite = NPCSpriteFamily.UID -Assign NPCSpriteFamily.f_mover = NPCMover.UID -Assign NPCSpriteFamily.f_autoTP 'autoTP' value of 'npc' -Position NPCMover on Tile -Instantiate that NPCSprite on Main layer (use Nickname to instantiate; see here) -Pin NPCSprite to NPCMover (no offset) -Scan for Tiled Objects with type 'talkpoint' -Query logical position of object -Query 'name' of 'talkpoint' -Select the Tile resides on logical position of 'talkpoint' at 0-layer -Assign Tile.talkpoint the 'name' of the 'talkpoint'
C2 Auto-placement of TalkPoints
Assuming there had been some NPCs with an ‘autoTP’ value of 1, this means that the system will generate automatic TalkPoints for that NPC. This happens after TMX loading.
-Loop through NPCSpriteFamily with .f_autoTP=1 -Select the NPCMover based on NPCSpriteFamily.f_mover -Determine the Tile ID that the NPCMover is on -Determine the Tiles that surround the Tile in question -Cull out the Tiles that are not accessible (impass=true) -Mark those Tiles with Tiles.talkpoint='Letigus' (NPCSpriteFamily.f_name)
IN ACTION IN C2
- When an NPCSpriteFamily is clicked, is intent=’talk='<npc_name>”?
- Yes: Query current Tile. Is Tile.talkpoint = NPCSpriteFamily.f_name?
- Yes: Talk.
- No: Is NPCSpriteFamily.f_autoTP=0? (Manually placed TalkPoints)
- Yes: Select Tile with Tile.UID = NPCSpriteFamily.f_talkpoint, and Pathfind to Tile
- No: Pathfind to NPC location
- No: Pathfind to NPC location
- Yes: Query current Tile. Is Tile.talkpoint = NPCSpriteFamily.f_name?
- On enter Tile
- Query if Tile.talkpoint != ”?
- Yes (it is a TalkPoint): Query if intent=’talk’ and if intent=’talk=<Tile.talkpoint>’ (intent is to talk, and to talk to that specific NPC)
- Yes: Stop and talk.
- No: do nothing.
- No (it is not a TalkPoint): do nothing.
- Yes (it is a TalkPoint): Query if intent=’talk’ and if intent=’talk=<Tile.talkpoint>’ (intent is to talk, and to talk to that specific NPC)
- Query if Tile.talkpoint != ”?
OTHER ACTIONABLE OBJECTS
Find other examples that use the same concept as ‘click here, move there’, like in the NPC, where clicking on the NPC will not necessarily Pathfind to the NPC, but to a TalkPoint.
ACTIONS
Actions upon Objects can consist of
- Talk
- Look (examine)
- Use (take/get)
Talk has been previously covered
Look
As with StorageDict, which is first example of the the persistent data implementation, we create a LookDict Object. A LookDict has the following variables
LookDict.alias='l#.r#' # string id and room id to uniquely identify itself; this is generated by self.name+self.room LookDict.name='l#' # string id for name only LookDict.room='r#' # string id for room only LookDict.looktimes=0 # number of times that this item has been looked on LookDict.textdict=-1 # the UID of the LookTextDict that this LookDict refers to
The first 3 variables are identical to StorageDict.
The .lookTimes variable is new, and this stores the number of times the associated Object has been looked at. This is in consideration that the description of something may change by the number of times the player decides to examine an Object. This is also stored in the LookDict (the one that will be saved with the game) so that the value can be persistent.
The .textDict variable is new, too. Because the nature of reading out a description potentially need long text, the best way is to use a CSV to write the text script out in a spreadsheet, instead of trying to do it in Tiled. So a LookTextDict Object is created to store static text from a CSV string. This LookTextDict is then associated with the LookDict that asked for it to be created. Both the LookDict and LookTextDict are persistent.
The LookTextDict have instance variables:
LookTextDict.alias = LookDict.alias LookTextDict.lookdict = LookDict.UID
At this point I’m not sure if .alias is needed, but it is important that the LookTextDict is associated with its LookText via .UID.
The LookTextDict (Dictionary) has a certain syntax with its keys.
LookTextDict['n:0'] = 'text to show by default' LookTextDict['n:3'] = 'text to show if player has looked on this object at least 3 times' LookTextDict['a:ate cupcake'] = 'text to show if the accomps "ate cupcake" is achieved'
In the case of the above, if the player has looked onto the object 2 times, then the text in [‘n:0’] is shown since it has not reached 3 times.
Accomps (Accomplishments) are tracked differently (in the Player Object itself) so there’s no need to track persistent data in LookDict at all.
Again, the difference between LookDict and LookTextDict:
- LookDict stores persistence data and is generated by the TMX Object ‘look’
- LookTextDict is a static Dictionary generated at the beginning of the game (irrespective of TMX loading). It is a Dict that is init based on the contents of a much wider global ‘Look Text description CSV file’. LookDict looks for its LookTextDict partner through alias identification; when LookDict finds its LookTextDict partner, they make a connection through populating their .textdict and .lookdict variables, respectively, with their partner’s UID.
Implementation
In Tiled, create an Object type ‘look’. There are no other properties needed, only that its name is important as a lookup value for the CSV.
Externally, create a CSV/spreadsheet referencing the Tiled Object ‘look’ name.
In C2, LookTextDict Objects are created based on the CSV. So at the beginning of the load, perhaps even before the TMX is loaded, LookTextDict Objects are created ready to be referenced by and ‘look’ Object looking for its partner.
(Consider that small Sprites even within the same Tile (or not necessarily corresponding to a Tile) need a Look description)
Use
Using a locked door as an example, the locked Door can be opened by:
- Secpass (InvItem)
- Accomps
In the case of InvItem, that hasn’t been designed yet.
Accomps are easy, because the lookup is in the global AccompsDict. If an accomps key is encountered, then that locked Door can be opened.
Portals as Doors
Since Portals contain the ‘destination’ property, Portals can be promoted to take more properties. But to keep the data concentrated on one place, let the extra properties be put in the PortalDict.
Portal.name PortalDict.destination PortalDict.locked # is the Portal locked? PortalDict.unlockreq # requirements to unlock this Portal
Note that the ‘destination’ has been put in the PortalDict, instead of the Portal.
Although .destination and .unlockreq may be considered as static data putting every relevant data in one Object is much clearer. So here we have a mix of static and persistent/volatile data in the PortalDict Object.
.locked should be persistent. And using the same principle as the others, PortalDict is created to keep track of the Portal’s data.
Currently, a Portal is read from the TMX, then a Portal Sprite is created in the Portal z-layer.
So now, when a Portal is read, it goes through the same procedure, but a PortalDict is created as well. I don’t think a Dictionary is needed to carry simple information, I’m just trying to be consistent with the C2 Object types. And besides, there may be further use of expanding the data it can hold, and a Dictionary is the best container for that.
PortalDict, thus, needs an .alias/.name/.room instance variable to identify itself uniquely. It also needs a pointer to the UID of the Portal Sprite it’s associated with
PortalDict.alias PortalDict.name PortalDict.room PortalDict.portal
Note that the current Portal Sprite considers ‘name’ to be what we now call ‘alias’ (eg ‘p1.r1’). This must be changed when we start implementing PortalDict.
Conversely, the Portal Sprite must have a PortalDict connection:
Portal.portaldict
So, to recap TMX loading:
- On TMX load Portal Objects are read.
- Portal Sprites are generated on the ‘Portal’ z-layer of the chess/tile.
- PortalDict is generated from Portal.alias. If PortalDict already exists, then make the connection by PortalDict.portal = Portal.UID, and Portal.portaldict = PortalDict.UID.
- If PortalDict does not exist, then create a new one; assign PortalDict.alias=Portal.alias, and trade UIDs between them (like in #3 above)
In action in C2:
- When the player clicks on a Door, the intent could be to open. But PC will approach first if not on the Tile itself.
- Regardless, an ‘enter’ action is needed to test the Portal’s accessibility.
- The Portal being queried will be Portal Object residing in the ‘Portal’ z-layer that the PC is currently on.
- Is Portal.locked=true?
- Yes: what is Portal.unlockreq?
- Parsing the unlockreq. Note that at the moment there are two possible ways of unlocking a Portal: by InvItem, and by Accomps. Use ‘i’ as the key for InvItem, and use ‘a’ as the key for Accomps. Eg: Portal.unlockreq=’a:ate cupcake’, or Portal.unlockreq=’i:cupcake’, or an AND combination: Portal.unlockreq=’i:coffee;a:ate cupcake’.
- No: enter the Portal destination
- Yes: what is Portal.unlockreq?