mirror of https://github.com/AxioDL/metaforce.git
Various AutoMapper stubs
This commit is contained in:
parent
62ce5286fe
commit
1ee38df1ba
|
@ -0,0 +1,6 @@
|
|||
#include "CAutoMapper.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
#ifndef __URDE_CAUTOMAPPER_HPP__
|
||||
#define __URDE_CAUTOMAPPER_HPP__
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
#include "CInGameTweakManagerBase.hpp"
|
||||
#include "zeus/CQuaternion.hpp"
|
||||
#include "zeus/CTransform.hpp"
|
||||
#include "zeus/CVector3f.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
class CFinalInput;
|
||||
class IWorld;
|
||||
class CMapWorldInfo;
|
||||
class CStateManager;
|
||||
enum class EInGameGuiState;
|
||||
class CAutoMapper
|
||||
{
|
||||
public:
|
||||
enum class EAutoMapperState
|
||||
{
|
||||
};
|
||||
struct SAutoMapperRenderState
|
||||
{
|
||||
};
|
||||
|
||||
class CAudioMenu
|
||||
{
|
||||
public:
|
||||
enum class EMenu
|
||||
{
|
||||
};
|
||||
struct SMenuOption
|
||||
{
|
||||
SMenuOption(const std::string&, s32, s32, s32);
|
||||
void BuildDrawableString();
|
||||
};
|
||||
|
||||
private:
|
||||
public:
|
||||
CAudioMenu()=default;
|
||||
void SetIsHighlighted(bool);
|
||||
void AddOption(const SMenuOption&);
|
||||
void ProcessControllerInput(const CFinalInput&);
|
||||
u32 GetSelectionIndex() const;
|
||||
const SMenuOption& GetOption(s32) const;
|
||||
SMenuOption& Option(s32);
|
||||
void SetSelectionIndex(s32);
|
||||
void Draw(EMenu) const;
|
||||
};
|
||||
|
||||
private:
|
||||
public:
|
||||
CAutoMapper(CStateManager&);
|
||||
bool CheckLoadComplete();
|
||||
bool CanLeaveMapScrean(const CStateManager&) const;
|
||||
float GetMapRotationX() const;
|
||||
float GetMapRotationZ() const;
|
||||
u32 GetFocusAreaIndex() const;
|
||||
ResId GetCurrWorldAssetId() const;
|
||||
void MuteAllLoopedSounds();
|
||||
void UnmuteAllLoopedSounds();
|
||||
void ProcessControllerInput(const CFinalInput&, CStateManager&);
|
||||
bool IsInPlayerControlState() const;
|
||||
void Draw(const CStateManager&, const zeus::CTransform&, float) const;
|
||||
bool IsInOrTransitioningToMapScreenState() const;
|
||||
float GetTimeIntoInterpolation() const;
|
||||
bool IsFullyInMapScreenState() const;
|
||||
void BeginMapperStateTransition(EAutoMapperState, const CStateManager&);
|
||||
void CompleteMapperStateTransition();
|
||||
void ResetInterpolationTimer(float);
|
||||
void BuildMiniMapWorldRenderState(const CStateManager&, const zeus::CQuaternion&, s32) const;
|
||||
void BuildMapScreenWorldRenderState(const CStateManager&, const zeus::CQuaternion&, s32) const;
|
||||
void BuildMapScreenUniverseRenderState(const CStateManager&, const zeus::CQuaternion&, s32) const;
|
||||
void SetShouldPanningSoundBePlaying(bool);
|
||||
void SetShouldZoomingSoundBePlaying(bool);
|
||||
void SetShouldRotatingSoundBePlaying(bool);
|
||||
void LeaveMapScreenState();
|
||||
void GetMiniMapCameraOrientation(CStateManager&);
|
||||
void GetAreaPointOfInterest(CStateManager, s32);
|
||||
void FindClosestVisibleArea(const zeus::CVector3f&, const zeus::CUnitVector3f&, const CStateManager&,
|
||||
const IWorld&, const CMapWorldInfo&) const;
|
||||
void FindClosestVisibleWorld(const zeus::CVector3f&, const zeus::CUnitVector3f&, const CStateManager&) const;
|
||||
|
||||
void GetMiniMapViewportSize();
|
||||
void GetMapScreenViewportSize();
|
||||
float GetMapAreaMiniDrawDepth();
|
||||
float GetMapAreaMaxDrawDepth();
|
||||
void GetMapAreaMiniMapDrawAlphaSurfaceVisited(const CStateManager&);
|
||||
void GetMapAreaMiniMapDrawAlphaOutlineVisited(const CStateManager&);
|
||||
void GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(const CStateManager&);
|
||||
void GetMapAreaMiniMapDrawAlphaOutlineUnvisited(const CStateManager&);
|
||||
void GetClampedMapScreenCameraDistance(float) const;
|
||||
void GetDesiredMiniMapCameraDistance(const CStateManager&) const;
|
||||
float GetBaseCameraMoveSpeed() const;
|
||||
float GetFinalCameraMoveSpeed() const;
|
||||
bool IsInMapperState(EAutoMapperState) const;
|
||||
bool IsInMapperTransition() const;
|
||||
bool IsRenderStateInterpolating() const;
|
||||
void TransformRenderStatesWorldToUniverse();
|
||||
void TransformRenderStatesUniverseToWorld();
|
||||
void TransformRenderStateWorldToUniverse(SAutoMapperRenderState&);
|
||||
void UpdateOptionsMenu(const CTweakValue::Audio&);
|
||||
void UpdateAudioMusicMenu();
|
||||
void UpdateAudioEvents();
|
||||
void UpdateAudioEventMenu();
|
||||
void GetCurrentAudioInfo() const;
|
||||
void PresentAudioMenuInput(const CFinalInput&);
|
||||
void SetFocusAudioMenu(CAudioMenu::EMenu);
|
||||
bool IsStateTransitioning() const;
|
||||
bool IsFullyInMiniMapState() const;
|
||||
static bool IsDrawState(EAutoMapperState);
|
||||
|
||||
void OnNewInGameGuiState(EInGameGuiState, const CStateManager&);
|
||||
void OnChangeAudioMusicSelection();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __URDE_CAUTOMAPPER_HPP__
|
|
@ -1,5 +1,9 @@
|
|||
set(AUTOMAPPER_SOURCES
|
||||
CMapWorldInfo.hpp CMapWorldInfo.cpp
|
||||
CMapWorld.hpp CMapWorld.cpp)
|
||||
CMapWorld.hpp CMapWorld.cpp
|
||||
CMapArea.hpp CMapArea.cpp
|
||||
CMappableObject.hpp CMappableObject.cpp
|
||||
CAutoMapper.hpp CAutoMapper.cpp
|
||||
ITweakAutoMapper.hpp)
|
||||
|
||||
runtime_add_list(AutoMapper AUTOMAPPER_SOURCES)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#include "CMapArea.hpp"
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef __URDE_CMAPAREA_HPP__
|
||||
#define __URDE_CMAPAREA_HPP__
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
#include "zeus/CAABox.hpp"
|
||||
#include "zeus/CVector3f.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
class CMapArea
|
||||
{
|
||||
public:
|
||||
class CMapAreaSurface
|
||||
{
|
||||
public:
|
||||
void PostConstruct(const void*);
|
||||
void Draw(const zeus::CVector3f*, const zeus::CColor&, const zeus::CColor&, float) const;
|
||||
const zeus::CVector3f& GetNormal() const;
|
||||
const zeus::CVector3f& GetCenterPosition() const;
|
||||
};
|
||||
|
||||
private:
|
||||
public:
|
||||
CMapArea(CInputStream&, uint);
|
||||
void PostConstruct();
|
||||
bool GetIsVisibleToAutoMapper(bool, bool) const;
|
||||
zeus::CVector3f GetAreaCenterPoint() const;
|
||||
zeus::CAABox GetBoundingBox() const;
|
||||
const zeus::CVector3f& GetVertices() const;
|
||||
void GetMappableObject(s32) const;
|
||||
void GetSurface(s32) const;
|
||||
u32 GetNumMappableObjects() const;
|
||||
u32 GetNumSurfaces() const;
|
||||
|
||||
};
|
||||
}
|
||||
#endif // __URDE_CMAPAREA_HPP__
|
|
@ -2,6 +2,7 @@
|
|||
#define __URDE_CMAPWORLD_HPP__
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
#include "CToken.hpp"
|
||||
#include "zeus/CColor.hpp"
|
||||
#include "zeus/CVector3f.hpp"
|
||||
#include "zeus/CTransform.hpp"
|
||||
|
@ -9,6 +10,7 @@
|
|||
namespace urde
|
||||
{
|
||||
class IWorld;
|
||||
class CMapArea;
|
||||
class CMapWorldInfo;
|
||||
class CStateManager;
|
||||
class CMapWorld
|
||||
|
@ -48,6 +50,7 @@ public:
|
|||
|
||||
class CMapAreaData
|
||||
{
|
||||
TCachedToken<CMapArea> x0_area;
|
||||
public:
|
||||
CMapAreaData(u32, EMapAreaList, CMapAreaData*);
|
||||
void Lock();
|
||||
|
@ -81,6 +84,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
std::vector<CMapAreaData> x0_areas;
|
||||
public:
|
||||
CMapWorld(CInputStream&);
|
||||
u32 GetNumAreas() const;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#include "CMappableObject.hpp"
|
||||
#include "ITweakAutoMapper.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
const zeus::CVector3f CMappableObject::skDoorVerts[8] = {};
|
||||
|
||||
void CMappableObject::ReadAutoMapperTweaks(const ITweakAutoMapper& tweaks)
|
||||
{
|
||||
const zeus::CVector3f& center = tweaks.GetDoorCenter();
|
||||
/* Ugly hack, but necessary */
|
||||
zeus::CVector3f* doorVerts = (zeus::CVector3f*)CMappableObject::skDoorVerts;
|
||||
/* Wrap door verts around -Z to build surface */
|
||||
doorVerts[0].assign( -center.z, -center.y, 0.f);
|
||||
doorVerts[1].assign( -center.z, -center.y, 2.f * center.x);
|
||||
doorVerts[2].assign( -center.z, center.y, 0.f);
|
||||
doorVerts[3].assign( -center.z, center.y, 2.f * center.x);
|
||||
doorVerts[4].assign(.2f * -center.z, -center.y, 0.f);
|
||||
doorVerts[5].assign(.2f * -center.z, -center.y, 2.f * center.x);
|
||||
doorVerts[6].assign(.2f * -center.z, center.y, 0.f);
|
||||
doorVerts[7].assign(.2f * -center.z, center.y, 2.f * center.x);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef __URDE_CMAPPABLEOBJECT_HPP__
|
||||
#define __URDE_CMAPPABLEOBJECT_HPP__
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
#include "zeus/CAABox.hpp"
|
||||
#include "zeus/CTransform.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
class CStateManager;
|
||||
class ITweakAutoMapper;
|
||||
class CMappableObject
|
||||
{
|
||||
public:
|
||||
enum class EMappableObjectType
|
||||
{
|
||||
BlueDoor = 0,
|
||||
ShieldDoor = 1,
|
||||
IceDoor = 2,
|
||||
WaveDoor = 3,
|
||||
PlasmaDoor = 4,
|
||||
BigDoor1 = 5,
|
||||
BigDoor2 = 6,
|
||||
IceDoorCeiling = 7,
|
||||
IceDoorFloor = 8,
|
||||
WaveDoorCeiling = 9,
|
||||
WaveDoorFloor = 10,
|
||||
IceDoorFloor2 = 13,
|
||||
WaveDoorFloor2 = 14,
|
||||
DownArrowYellow = 27, /* Maintenance Tunnel */
|
||||
UpArrowYellow = 28, /* Phazon Processing Center */
|
||||
DownArrowGreen = 29, /* Elevator A */
|
||||
UpArrowGreen = 30, /* Elite Control Access */
|
||||
DownArrowRed = 31, /* Elevator B */
|
||||
UpArrowRed = 32, /* Fungal Hall Access */
|
||||
TransportLift = 33,
|
||||
SaveStation = 34,
|
||||
MissileStation = 37
|
||||
};
|
||||
|
||||
private:
|
||||
static const zeus::CVector3f skDoorVerts[8];
|
||||
|
||||
public:
|
||||
void PostConstruct(const void*);
|
||||
const zeus::CTransform& GetTransform() const;
|
||||
EMappableObjectType GetType() const;
|
||||
void Draw(int, const CStateManager&, float, bool) const;
|
||||
void DrawDoorSurface(int, const CStateManager&, float, int, bool) const;
|
||||
void BuildSurfaceCenterPoint(s32) const;
|
||||
bool IsDoorConnectedToArea(s32, const CStateManager&) const;
|
||||
bool IsDoorConnectedToVisitedArea(const CStateManager&) const;
|
||||
bool GetIsVisibleToAutoMapper(bool) const;
|
||||
bool GetIsSeen() const;
|
||||
|
||||
void ReadAutoMapperTweaks(const ITweakAutoMapper&);
|
||||
static bool GetTweakIsMapVisibilityCheat();
|
||||
static bool IsDoorType(EMappableObjectType);
|
||||
};
|
||||
}
|
||||
#endif // __URDE_CMAPPABLEOBJECT_HPP__
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef __URDE_ITWEAKAUTOMAPPER_HPP__
|
||||
#define __URDE_ITWEAKAUTOMAPPER_HPP__
|
||||
|
||||
#include "zeus/CVector3f.hpp"
|
||||
#include "ITweak.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
class ITweakAutoMapper : public ITweak
|
||||
{
|
||||
public:
|
||||
virtual ~ITweakAutoMapper() {}
|
||||
virtual const zeus::CVector3f& GetDoorCenter() const=0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __URDE_ITWEAKAUTOMAPPER_HPP__
|
|
@ -4,16 +4,36 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
|
||||
struct CTweakValue
|
||||
class CTweakValue
|
||||
{
|
||||
public:
|
||||
struct Audio
|
||||
{
|
||||
Audio(float, float, float, const std::string&, u32);
|
||||
void None();
|
||||
};
|
||||
enum class EType
|
||||
{
|
||||
} x0_type;
|
||||
};
|
||||
|
||||
|
||||
EType x0_type;
|
||||
std::string x4_key;
|
||||
std::string x30_str;
|
||||
public:
|
||||
CTweakValue()=default;
|
||||
//CTweakValue(CTextInputStream&);
|
||||
//void PutTo(CTextOutStream&);
|
||||
const std::string& GetName() const { return x4_key; }
|
||||
const std::string& GetValueAsString() const;
|
||||
void SetValueFromString(const std::string&);
|
||||
const Audio& GetAudio() const;
|
||||
EType GetType() const { return x0_type; }
|
||||
};
|
||||
|
||||
class CInGameTweakManagerBase
|
||||
|
@ -24,7 +44,7 @@ public:
|
|||
bool HasTweakValue(const std::string& name) const
|
||||
{
|
||||
for (const CTweakValue& val : x0_values)
|
||||
if (val.x4_key == name)
|
||||
if (val.GetName() == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -32,7 +52,7 @@ public:
|
|||
const CTweakValue* GetTweakValue(const std::string& name) const
|
||||
{
|
||||
for (const CTweakValue& val : x0_values)
|
||||
if (val.x4_key == name)
|
||||
if (val.GetName() == name)
|
||||
return &val;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ include_directories(. ..)
|
|||
set(MP1_SOURCES
|
||||
CTweaks.hpp CTweaks.cpp
|
||||
CInGameTweakManager.hpp CInGameTweakManager.cpp
|
||||
Tweaks/CTweakAutoMapper.hpp Tweaks/CTweakAutoMapper.cpp
|
||||
Tweaks/CTweakPlayer.hpp Tweaks/CTweakPlayer.cpp
|
||||
CMainFlow.hpp CMainFlow.cpp
|
||||
CMFGame.hpp CMFGame.cpp
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
#include "CTweakAutoMapper.hpp"
|
||||
namespace urde
|
||||
{
|
||||
namespace MP1
|
||||
{
|
||||
CTweakAutoMapper::CTweakAutoMapper(CInputStream& in)
|
||||
{
|
||||
x4_24_ = in.readBool();
|
||||
x4_25_ = in.readBool();
|
||||
x4_26_ = in.readBool();
|
||||
x8_ = in.readFloatBig();
|
||||
xc_ = in.readFloatBig();
|
||||
x10_ = in.readFloatBig();
|
||||
x14_ = in.readFloatBig();
|
||||
x18_ = in.readFloatBig();
|
||||
x1c_ = in.readFloatBig();
|
||||
x20_ = in.readFloatBig();
|
||||
x24_.readRGBABig(in);
|
||||
x28_ = in.readFloatBig();
|
||||
x2c_ = in.readFloatBig();
|
||||
x30_ = in.readFloatBig();
|
||||
x34_ = in.readFloatBig();
|
||||
x38_.readRGBABig(in);
|
||||
x3c_.readRGBABig(in);
|
||||
x40_.readRGBABig(in);
|
||||
x44_.readRGBABig(in);
|
||||
x48_.readRGBABig(in);
|
||||
x4c_.readRGBABig(in);
|
||||
x50_.readRGBABig(in);
|
||||
x54_ = in.readFloatBig();
|
||||
x58_ = in.readFloatBig();
|
||||
x5c_ = in.readFloatBig();
|
||||
x64_ = in.readFloatBig();
|
||||
x68_ = in.readFloatBig();
|
||||
x6c_ = in.readFloatBig();
|
||||
x70_ = in.readFloatBig();
|
||||
x74_ = in.readFloatBig();
|
||||
x78_ = in.readFloatBig();
|
||||
x7c_.readRGBABig(in);
|
||||
x80_.readRGBABig(in);
|
||||
x84_ = in.readFloatBig();
|
||||
x88_ = in.readFloatBig();
|
||||
x8c_ = in.readFloatBig();
|
||||
x90_ = in.readFloatBig();
|
||||
x94_ = in.readFloatBig();
|
||||
x98_ = in.readFloatBig();
|
||||
x9c_ = in.readFloatBig();
|
||||
xa0_ = in.readFloatBig();
|
||||
/* Originally 4 separate floats */
|
||||
xa4_doorCenter.readBig(in);
|
||||
xb0_ = in.readFloatBig();
|
||||
xb4_ = in.readFloatBig();
|
||||
xb8_ = in.readFloatBig();
|
||||
xbc_ = in.readFloatBig();
|
||||
xc0_ = in.readFloatBig();
|
||||
xc4_ = in.readFloatBig();
|
||||
xc8_ = in.readFloatBig();
|
||||
xcc_ = in.readBool();
|
||||
xd0_ = in.readFloatBig();
|
||||
xd4_ = in.readFloatBig();
|
||||
xd8_ = in.readFloatBig();
|
||||
xdc_ = in.readFloatBig();
|
||||
xe0_ = in.readFloatBig();
|
||||
xe4_ = in.readFloatBig();
|
||||
xe8_ = in.readFloatBig();
|
||||
xec_ = in.readFloatBig();
|
||||
xf0_.readRGBABig(in);
|
||||
xf4_.readRGBABig(in);
|
||||
xf8_.readRGBABig(in);
|
||||
xfc_.readRGBABig(in);
|
||||
x100_doorColorCount = in.readUint32Big();
|
||||
for (u32 i = 0 ; i<x100_doorColorCount ; ++i)
|
||||
{
|
||||
x104_doorColors.emplace_back();
|
||||
x104_doorColors.back().readRGBABig(in);
|
||||
}
|
||||
x118_doorBorderColor.readRGBABig(in);
|
||||
x11c_openDoorColor.readRGBABig(in);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef __URDE_MP1_CTWEAKAUTOMAPPER_HPP__
|
||||
#define __URDE_MP1_CTWEAKAUTOMAPPER_HPP__
|
||||
|
||||
#include "RetroTypes.hpp"
|
||||
#include "AutoMapper/ITweakAutoMapper.hpp"
|
||||
#include "zeus/CColor.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
namespace MP1
|
||||
{
|
||||
class CTweakAutoMapper : ITweakAutoMapper
|
||||
{
|
||||
private:
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool x4_24_ : 1;
|
||||
bool x4_25_ : 1;
|
||||
bool x4_26_ : 1;
|
||||
};
|
||||
};
|
||||
float x8_;
|
||||
float xc_;
|
||||
float x10_;
|
||||
float x14_;
|
||||
float x18_;
|
||||
float x1c_;
|
||||
float x20_;
|
||||
zeus::CColor x24_;
|
||||
float x28_;
|
||||
float x2c_;
|
||||
float x30_;
|
||||
float x34_;
|
||||
zeus::CColor x38_;
|
||||
zeus::CColor x3c_;
|
||||
zeus::CColor x40_;
|
||||
zeus::CColor x44_;
|
||||
zeus::CColor x48_;
|
||||
zeus::CColor x4c_;
|
||||
zeus::CColor x50_;
|
||||
float x54_;
|
||||
float x58_;
|
||||
float x5c_;
|
||||
float x60_ = 0.4f;
|
||||
float x64_;
|
||||
float x68_;
|
||||
float x6c_;
|
||||
float x70_;
|
||||
float x74_;
|
||||
float x78_;
|
||||
zeus::CColor x7c_;
|
||||
zeus::CColor x80_;
|
||||
float x84_;
|
||||
float x88_;
|
||||
float x8c_;
|
||||
float x90_;
|
||||
float x94_;
|
||||
float x98_;
|
||||
float x9c_;
|
||||
float xa0_;
|
||||
/* Originally 4 separate floats */
|
||||
zeus::CVector3f xa4_doorCenter;
|
||||
float xb0_;
|
||||
float xb4_;
|
||||
float xb8_;
|
||||
float xbc_;
|
||||
float xc0_;
|
||||
float xc4_;
|
||||
float xc8_;
|
||||
bool xcc_;
|
||||
float xd0_;
|
||||
float xd4_;
|
||||
float xd8_;
|
||||
float xdc_;
|
||||
float xe0_;
|
||||
float xe4_;
|
||||
float xe8_;
|
||||
float xec_;
|
||||
zeus::CColor xf0_;
|
||||
zeus::CColor xf4_;
|
||||
zeus::CColor xf8_;
|
||||
zeus::CColor xfc_;
|
||||
u32 x100_doorColorCount;
|
||||
std::vector<zeus::CColor> x104_doorColors;
|
||||
zeus::CColor x118_doorBorderColor;
|
||||
zeus::CColor x11c_openDoorColor;
|
||||
public:
|
||||
CTweakAutoMapper(CInputStream&);
|
||||
const zeus::CVector3f& GetDoorCenter() const { return xa4_doorCenter; }
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // __URDE_MP1_CTWEAKAUTOMAPPER_HPP__
|
Loading…
Reference in New Issue