mirror of
https://github.com/ZDoom/Raze.git
synced 2025-06-01 17:52:13 +00:00
- Get mouse/controller input by pointed variable and not copy on return.
This commit is contained in:
parent
1f97e73501
commit
446218dd7b
15 changed files with 51 additions and 45 deletions
|
@ -110,7 +110,7 @@ void resetTurnHeldAmt()
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void processMovement(InputPacket* const currInput, InputPacket* const inputBuffer, ControlInfo* const hidInput, const double scaleAdjust, const int drink_amt, const bool allowstrafe, const double turnscale)
|
void processMovement(InputPacket* const currInput, InputPacket* const inputBuffer, HIDInput* const hidInput, const double scaleAdjust, const int drink_amt, const bool allowstrafe, const double turnscale)
|
||||||
{
|
{
|
||||||
// set up variables.
|
// set up variables.
|
||||||
const int keymove = 1 << int(!!(inputBuffer->actions & SB_RUN));
|
const int keymove = 1 << int(!!(inputBuffer->actions & SB_RUN));
|
||||||
|
|
|
@ -83,4 +83,4 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerAngles& w, P
|
||||||
void updateTurnHeldAmt(const double scaleAdjust);
|
void updateTurnHeldAmt(const double scaleAdjust);
|
||||||
bool isTurboTurnTime();
|
bool isTurboTurnTime();
|
||||||
void resetTurnHeldAmt();
|
void resetTurnHeldAmt();
|
||||||
void processMovement(InputPacket* const currInput, InputPacket* const inputBuffer, ControlInfo* const hidInput, const double scaleAdjust, const int drink_amt = 0, const bool allowstrafe = true, const double turnscale = 1);
|
void processMovement(InputPacket* const currInput, InputPacket* const inputBuffer, HIDInput* const hidInput, const double scaleAdjust, const int drink_amt = 0, const bool allowstrafe = true, const double turnscale = 1);
|
||||||
|
|
|
@ -88,7 +88,7 @@ struct GameInterface
|
||||||
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
|
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
|
||||||
virtual void SetAmbience(bool on) {}
|
virtual void SetAmbience(bool on) {}
|
||||||
virtual void ExitFromMenu() { throw CExitEvent(0); }
|
virtual void ExitFromMenu() { throw CExitEvent(0); }
|
||||||
virtual void GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet = nullptr) {}
|
virtual void GetInput(const double scaleAdjust, InputPacket* packet = nullptr) {}
|
||||||
virtual void UpdateSounds() {}
|
virtual void UpdateSounds() {}
|
||||||
virtual void ErrorCleanup() {}
|
virtual void ErrorCleanup() {}
|
||||||
virtual void Startup() {}
|
virtual void Startup() {}
|
||||||
|
|
|
@ -65,7 +65,7 @@ CVARD(Bool, invertmouse, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert vertic
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void InputState::GetMouseDelta(ControlInfo * hidInput)
|
void InputState::GetMouseDelta(HIDInput * hidInput)
|
||||||
{
|
{
|
||||||
g_mousePos *= backendinputscale();
|
g_mousePos *= backendinputscale();
|
||||||
|
|
||||||
|
@ -201,11 +201,9 @@ int32_t handleevents(void)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
ControlInfo CONTROL_GetInput()
|
void getHidInput(HIDInput* const hidInput)
|
||||||
{
|
{
|
||||||
ControlInfo hidInput {};
|
inputState.GetMouseDelta(hidInput);
|
||||||
|
|
||||||
inputState.GetMouseDelta(&hidInput);
|
|
||||||
|
|
||||||
if (use_joystick)
|
if (use_joystick)
|
||||||
{
|
{
|
||||||
|
@ -214,13 +212,11 @@ ControlInfo CONTROL_GetInput()
|
||||||
|
|
||||||
I_GetAxes(joyaxes);
|
I_GetAxes(joyaxes);
|
||||||
|
|
||||||
hidInput.dyaw += -joyaxes[JOYAXIS_Yaw];
|
hidInput->dyaw += -joyaxes[JOYAXIS_Yaw];
|
||||||
hidInput.dpitch += -joyaxes[JOYAXIS_Pitch];
|
hidInput->dpitch += -joyaxes[JOYAXIS_Pitch];
|
||||||
hidInput.dforward += joyaxes[JOYAXIS_Forward] * .5f;
|
hidInput->dforward += joyaxes[JOYAXIS_Forward] * .5f;
|
||||||
hidInput.dside += joyaxes[JOYAXIS_Side] * .5f;
|
hidInput->dside += joyaxes[JOYAXIS_Side] * .5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hidInput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -398,7 +394,7 @@ CCMD(show_weapon)
|
||||||
gi->ToggleShowWeapon();
|
gi->ToggleShowWeapon();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyGlobalInput(InputPacket& input, ControlInfo* hidInput, bool const crouchable, bool const disableToggle)
|
void ApplyGlobalInput(InputPacket& input, HIDInput* hidInput, bool const crouchable, bool const disableToggle)
|
||||||
{
|
{
|
||||||
if (WeaponToSend != 0) input.setNewWeapon(WeaponToSend);
|
if (WeaponToSend != 0) input.setNewWeapon(WeaponToSend);
|
||||||
WeaponToSend = 0;
|
WeaponToSend = 0;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "vectors.h"
|
#include "vectors.h"
|
||||||
|
|
||||||
|
|
||||||
struct ControlInfo
|
struct HIDInput
|
||||||
{
|
{
|
||||||
float dside;
|
float dside;
|
||||||
float dup;
|
float dup;
|
||||||
|
@ -50,7 +50,7 @@ public:
|
||||||
g_mousePos.Y += y;
|
g_mousePos.Y += y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetMouseDelta(ControlInfo* hidInput);
|
void GetMouseDelta(HIDInput* hidInput);
|
||||||
|
|
||||||
void ClearAllInput();
|
void ClearAllInput();
|
||||||
bool CheckAllInput()
|
bool CheckAllInput()
|
||||||
|
@ -63,7 +63,7 @@ public:
|
||||||
|
|
||||||
extern InputState inputState;
|
extern InputState inputState;
|
||||||
|
|
||||||
ControlInfo CONTROL_GetInput();
|
void getHidInput(HIDInput* const hidInput);
|
||||||
int32_t handleevents(void);
|
int32_t handleevents(void);
|
||||||
|
|
||||||
enum GameFunction_t
|
enum GameFunction_t
|
||||||
|
@ -102,7 +102,7 @@ enum GameFunction_t
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetupGameButtons();
|
void SetupGameButtons();
|
||||||
void ApplyGlobalInput(InputPacket& input, ControlInfo* const hidInput, bool const crouchable = true, bool const disableToggle = false);
|
void ApplyGlobalInput(InputPacket& input, HIDInput* const hidInput, bool const crouchable = true, bool const disableToggle = false);
|
||||||
extern ESyncBits ActionsToSend;
|
extern ESyncBits ActionsToSend;
|
||||||
extern bool gamesetinput;
|
extern bool gamesetinput;
|
||||||
|
|
||||||
|
|
|
@ -136,8 +136,7 @@ void G_BuildTiccmd(ticcmd_t* cmd)
|
||||||
}
|
}
|
||||||
cmd->ucmd = {};
|
cmd->ucmd = {};
|
||||||
I_GetEvent();
|
I_GetEvent();
|
||||||
auto input = CONTROL_GetInput();
|
gi->GetInput(inputScale, &cmd->ucmd);
|
||||||
gi->GetInput(&input, inputScale, &cmd->ucmd);
|
|
||||||
cmd->consistency = consistency[myconnectindex][(maketic / ticdup) % BACKUPTICS];
|
cmd->consistency = consistency[myconnectindex][(maketic / ticdup) % BACKUPTICS];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,8 +614,7 @@ void TryRunTics (void)
|
||||||
if (!SyncInput())
|
if (!SyncInput())
|
||||||
{
|
{
|
||||||
I_GetEvent();
|
I_GetEvent();
|
||||||
auto input = CONTROL_GetInput();
|
gi->GetInput(inputScale);
|
||||||
gi->GetInput(&input, inputScale);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ struct GameInterface : public ::GameInterface
|
||||||
void MenuClosed() override;
|
void MenuClosed() override;
|
||||||
bool CanSave() override;
|
bool CanSave() override;
|
||||||
void UpdateSounds() override;
|
void UpdateSounds() override;
|
||||||
void GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet = nullptr) override;
|
void GetInput(const double scaleAdjust, InputPacket* packet = nullptr) override;
|
||||||
void Ticker() override;
|
void Ticker() override;
|
||||||
void DrawBackground() override;
|
void DrawBackground() override;
|
||||||
void Startup() override;
|
void Startup() override;
|
||||||
|
|
|
@ -40,7 +40,7 @@ static InputPacket gInput;
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet)
|
void GameInterface::GetInput(const double scaleAdjust, InputPacket* packet)
|
||||||
{
|
{
|
||||||
if (paused || M_Active())
|
if (paused || M_Active())
|
||||||
{
|
{
|
||||||
|
@ -48,11 +48,14 @@ void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdju
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HIDInput hidInput;
|
||||||
|
getHidInput(&hidInput);
|
||||||
|
|
||||||
PLAYER* pPlayer = &gPlayer[myconnectindex];
|
PLAYER* pPlayer = &gPlayer[myconnectindex];
|
||||||
InputPacket input{};
|
InputPacket input{};
|
||||||
|
|
||||||
ApplyGlobalInput(gInput, hidInput);
|
ApplyGlobalInput(gInput, &hidInput);
|
||||||
processMovement(&input, &gInput, hidInput, scaleAdjust);
|
processMovement(&input, &gInput, &hidInput, scaleAdjust);
|
||||||
|
|
||||||
// Perform unsynchronised angle/horizon if not dead.
|
// Perform unsynchronised angle/horizon if not dead.
|
||||||
if (!SyncInput() && gamestate == GS_LEVEL && pPlayer->actor->xspr.health != 0)
|
if (!SyncInput() && gamestate == GS_LEVEL && pPlayer->actor->xspr.health != 0)
|
||||||
|
|
|
@ -38,7 +38,7 @@ struct GameInterface : public ::GameInterface
|
||||||
void SerializeGameState(FSerializer& arc) override;
|
void SerializeGameState(FSerializer& arc) override;
|
||||||
void ExitFromMenu() override;
|
void ExitFromMenu() override;
|
||||||
void DrawPlayerSprite(const DVector2& origin, bool onteam) override;
|
void DrawPlayerSprite(const DVector2& origin, bool onteam) override;
|
||||||
void GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet = nullptr) override;
|
void GetInput(const double scaleAdjust, InputPacket* packet = nullptr) override;
|
||||||
void UpdateSounds() override;
|
void UpdateSounds() override;
|
||||||
void Startup() override;
|
void Startup() override;
|
||||||
void DrawBackground() override;
|
void DrawBackground() override;
|
||||||
|
|
|
@ -523,7 +523,7 @@ static constexpr float VEHICLETURN = (20.f * 360.f / 2048.f);
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
static void processInputBits(player_struct *p, ControlInfo* const hidInput)
|
static void processInputBits(player_struct *p, HIDInput* const hidInput)
|
||||||
{
|
{
|
||||||
// Set-up crouch bools.
|
// Set-up crouch bools.
|
||||||
int const sectorLotag = p->insector() ? p->cursector->lotag : 0;
|
int const sectorLotag = p->insector() ? p->cursector->lotag : 0;
|
||||||
|
@ -555,7 +555,7 @@ static void processInputBits(player_struct *p, ControlInfo* const hidInput)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
static float motoApplyTurn(player_struct* p, ControlInfo* const hidInput, bool const kbdLeft, bool const kbdRight, const float factor)
|
static float motoApplyTurn(player_struct* p, HIDInput* const hidInput, bool const kbdLeft, bool const kbdRight, const float factor)
|
||||||
{
|
{
|
||||||
float turnvel = 0;
|
float turnvel = 0;
|
||||||
p->oTiltStatus = p->TiltStatus;
|
p->oTiltStatus = p->TiltStatus;
|
||||||
|
@ -645,7 +645,7 @@ static float motoApplyTurn(player_struct* p, ControlInfo* const hidInput, bool c
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
static float boatApplyTurn(player_struct *p, ControlInfo* const hidInput, bool const kbdLeft, bool const kbdRight, const float factor)
|
static float boatApplyTurn(player_struct *p, HIDInput* const hidInput, bool const kbdLeft, bool const kbdRight, const float factor)
|
||||||
{
|
{
|
||||||
float turnvel = 0;
|
float turnvel = 0;
|
||||||
p->oTiltStatus = p->TiltStatus;
|
p->oTiltStatus = p->TiltStatus;
|
||||||
|
@ -731,7 +731,7 @@ static float boatApplyTurn(player_struct *p, ControlInfo* const hidInput, bool c
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
static void processVehicleInput(player_struct *p, ControlInfo* const hidInput, InputPacket& input, double const scaleAdjust)
|
static void processVehicleInput(player_struct *p, HIDInput* const hidInput, InputPacket& input, double const scaleAdjust)
|
||||||
{
|
{
|
||||||
bool const kbdLeft = buttonMap.ButtonDown(gamefunc_Turn_Left) || buttonMap.ButtonDown(gamefunc_Strafe_Left);
|
bool const kbdLeft = buttonMap.ButtonDown(gamefunc_Turn_Left) || buttonMap.ButtonDown(gamefunc_Strafe_Left);
|
||||||
bool const kbdRight = buttonMap.ButtonDown(gamefunc_Turn_Right) || buttonMap.ButtonDown(gamefunc_Strafe_Right);
|
bool const kbdRight = buttonMap.ButtonDown(gamefunc_Turn_Right) || buttonMap.ButtonDown(gamefunc_Strafe_Right);
|
||||||
|
@ -805,7 +805,7 @@ static void FinalizeInput(player_struct *p, InputPacket& input)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet)
|
void GameInterface::GetInput(const double scaleAdjust, InputPacket* packet)
|
||||||
{
|
{
|
||||||
if (paused || gamestate != GS_LEVEL)
|
if (paused || gamestate != GS_LEVEL)
|
||||||
{
|
{
|
||||||
|
@ -813,18 +813,21 @@ void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdju
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HIDInput hidInput;
|
||||||
|
getHidInput(&hidInput);
|
||||||
|
|
||||||
auto const p = &ps[myconnectindex];
|
auto const p = &ps[myconnectindex];
|
||||||
InputPacket input{};
|
InputPacket input{};
|
||||||
|
|
||||||
processInputBits(p, hidInput);
|
processInputBits(p, &hidInput);
|
||||||
|
|
||||||
if (isRRRA() && (p->OnMotorcycle || p->OnBoat))
|
if (isRRRA() && (p->OnMotorcycle || p->OnBoat))
|
||||||
{
|
{
|
||||||
processVehicleInput(p, hidInput, input, scaleAdjust);
|
processVehicleInput(p, &hidInput, input, scaleAdjust);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
processMovement(&input, &loc, hidInput, scaleAdjust, p->drink_amt);
|
processMovement(&input, &loc, &hidInput, scaleAdjust, p->drink_amt);
|
||||||
}
|
}
|
||||||
|
|
||||||
FinalizeInput(p, input);
|
FinalizeInput(p, input);
|
||||||
|
|
|
@ -225,7 +225,7 @@ struct GameInterface : public ::GameInterface
|
||||||
void DrawBackground() override;
|
void DrawBackground() override;
|
||||||
void Render() override;
|
void Render() override;
|
||||||
//void DrawWeapons() override;
|
//void DrawWeapons() override;
|
||||||
void GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet = nullptr) override;
|
void GetInput(const double scaleAdjust, InputPacket* packet = nullptr) override;
|
||||||
void Startup() override;
|
void Startup() override;
|
||||||
const char* GenericCheat(int player, int cheat) override;
|
const char* GenericCheat(int player, int cheat) override;
|
||||||
void NewGame(MapRecord *map, int skill, bool) override;
|
void NewGame(MapRecord *map, int skill, bool) override;
|
||||||
|
|
|
@ -46,7 +46,7 @@ void ClearSpaceBar(int nPlayer)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* packet)
|
void GameInterface::GetInput(const double scaleAdjust, InputPacket* packet)
|
||||||
{
|
{
|
||||||
if (paused || M_Active())
|
if (paused || M_Active())
|
||||||
{
|
{
|
||||||
|
@ -54,10 +54,13 @@ void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdju
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HIDInput hidInput;
|
||||||
|
getHidInput(&hidInput);
|
||||||
|
|
||||||
if (packet != nullptr)
|
if (packet != nullptr)
|
||||||
{
|
{
|
||||||
localInput = {};
|
localInput = {};
|
||||||
ApplyGlobalInput(localInput, hidInput);
|
ApplyGlobalInput(localInput, &hidInput);
|
||||||
if (PlayerList[nLocalPlayer].nHealth == 0) localInput.actions &= SB_OPEN;
|
if (PlayerList[nLocalPlayer].nHealth == 0) localInput.actions &= SB_OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdju
|
||||||
|
|
||||||
if (PlayerList[nLocalPlayer].nHealth != 0)
|
if (PlayerList[nLocalPlayer].nHealth != 0)
|
||||||
{
|
{
|
||||||
processMovement(&input, &localInput, hidInput, scaleAdjust);
|
processMovement(&input, &localInput, &hidInput, scaleAdjust);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1681,7 +1681,7 @@ struct GameInterface : public ::GameInterface
|
||||||
void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); }
|
void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); }
|
||||||
void UpdateSounds() override;
|
void UpdateSounds() override;
|
||||||
void ErrorCleanup() override;
|
void ErrorCleanup() override;
|
||||||
void GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket* input = nullptr) override;
|
void GetInput(const double scaleAdjust, InputPacket* input = nullptr) override;
|
||||||
void DrawBackground(void) override;
|
void DrawBackground(void) override;
|
||||||
void Ticker(void) override;
|
void Ticker(void) override;
|
||||||
void Render() override;
|
void Render() override;
|
||||||
|
|
|
@ -160,7 +160,7 @@ static void processWeapon(PLAYER* const pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdjust, InputPacket *packet)
|
void GameInterface::GetInput(const double scaleAdjust, InputPacket *packet)
|
||||||
{
|
{
|
||||||
PLAYER* pp = &Player[myconnectindex];
|
PLAYER* pp = &Player[myconnectindex];
|
||||||
|
|
||||||
|
@ -170,10 +170,13 @@ void GameInterface::GetInput(ControlInfo* const hidInput, double const scaleAdju
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HIDInput hidInput;
|
||||||
|
getHidInput(&hidInput);
|
||||||
|
|
||||||
InputPacket input {};
|
InputPacket input {};
|
||||||
|
|
||||||
ApplyGlobalInput(loc, hidInput);
|
ApplyGlobalInput(loc, &hidInput);
|
||||||
processMovement(&input, &loc, hidInput, scaleAdjust, 0, !pp->sop, pp->sop_control ? 3. / 1.40625 : 1.);
|
processMovement(&input, &loc, &hidInput, scaleAdjust, 0, !pp->sop, pp->sop_control ? 3. / 1.40625 : 1.);
|
||||||
processWeapon(pp);
|
processWeapon(pp);
|
||||||
|
|
||||||
if (!SyncInput())
|
if (!SyncInput())
|
||||||
|
|
|
@ -125,7 +125,7 @@ void DoPlayer(void);
|
||||||
void domovethings(void);
|
void domovethings(void);
|
||||||
void InitAllPlayers(void);
|
void InitAllPlayers(void);
|
||||||
void InitMultiPlayerInfo(const DVector3& spawnpos, const DAngle startang);
|
void InitMultiPlayerInfo(const DVector3& spawnpos, const DAngle startang);
|
||||||
void MoveScrollMode2D(PLAYER* pp, ControlInfo* const hidInput);
|
void MoveScrollMode2D(PLAYER* pp, HIDInput* const hidInput);
|
||||||
void DoPlayerDivePalette(PLAYER* pp);
|
void DoPlayerDivePalette(PLAYER* pp);
|
||||||
void DoPlayerNightVisionPalette(PLAYER* pp);
|
void DoPlayerNightVisionPalette(PLAYER* pp);
|
||||||
void DoPlayerStopDiveNoWarp(PLAYER* pp);
|
void DoPlayerStopDiveNoWarp(PLAYER* pp);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue