Merge branch 'master' of github.com:supertuxkart/stk-code
This commit is contained in:
commit
a72ed7ef30
@ -73,7 +73,11 @@
|
|||||||
|
|
||||||
<spacer height="4" width="10" />
|
<spacer height="4" width="10" />
|
||||||
|
|
||||||
<!-- Nothing here yet -->
|
<div layout="horizontal-row" proportion="1" height="fit">
|
||||||
|
<checkbox id="split_screen_horizontally"/>
|
||||||
|
<spacer width="20" height="100%" />
|
||||||
|
<label height="100%" I18N="In the ui settings" text="Multiplayer splits screen horizontally"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<spacer height="18" width="4"/>
|
<spacer height="18" width="4"/>
|
||||||
|
@ -129,6 +129,9 @@ when the border that intersect at this corner are enabled.
|
|||||||
hborder_out_portion="0.2" />
|
hborder_out_portion="0.2" />
|
||||||
|
|
||||||
<!-- Stateless -->
|
<!-- Stateless -->
|
||||||
|
<element type="squareFocusHaloBW" image="forest/glass_square_focused_bw.png"
|
||||||
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
|
hborder_out_portion="1.0" />
|
||||||
<element type="squareFocusHalo" image="forest/glass_square_focused.png"
|
<element type="squareFocusHalo" image="forest/glass_square_focused.png"
|
||||||
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
hborder_out_portion="1.0" />
|
hborder_out_portion="1.0" />
|
||||||
|
@ -128,6 +128,9 @@ when the border that intersect at this corner are enabled.
|
|||||||
hborder_out_portion="0.2" />
|
hborder_out_portion="0.2" />
|
||||||
|
|
||||||
<!-- Stateless -->
|
<!-- Stateless -->
|
||||||
|
<element type="squareFocusHaloBW" image="ocean/glass_square_focused_bw.png"
|
||||||
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
|
hborder_out_portion="1.0" />
|
||||||
<element type="squareFocusHalo" image="ocean/glass_square_focused.png"
|
<element type="squareFocusHalo" image="ocean/glass_square_focused.png"
|
||||||
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
hborder_out_portion="1.0" />
|
hborder_out_portion="1.0" />
|
||||||
|
@ -129,6 +129,9 @@ when the border that intersect at this corner are enabled.
|
|||||||
|
|
||||||
|
|
||||||
<!-- Stateless -->
|
<!-- Stateless -->
|
||||||
|
<element type="squareFocusHaloBW" image="peach/glass_square_focused_bw.png"
|
||||||
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
|
hborder_out_portion="1.0" />
|
||||||
<element type="squareFocusHalo" image="peach/glass_square_focused.png"
|
<element type="squareFocusHalo" image="peach/glass_square_focused.png"
|
||||||
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
hborder_out_portion="1.0" />
|
hborder_out_portion="1.0" />
|
||||||
|
@ -130,6 +130,9 @@ when the border that intersect at this corner are enabled.
|
|||||||
|
|
||||||
|
|
||||||
<!-- Stateless -->
|
<!-- Stateless -->
|
||||||
|
<element type="squareFocusHaloBW" image="ruby/glass_square_focused_bw.png"
|
||||||
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
|
hborder_out_portion="1.0" />
|
||||||
<element type="squareFocusHalo" image="ruby/glass_square_focused.png"
|
<element type="squareFocusHalo" image="ruby/glass_square_focused.png"
|
||||||
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
left_border="6" right_border ="6" top_border="6" bottom_border="6"
|
||||||
hborder_out_portion="1.0" />
|
hborder_out_portion="1.0" />
|
||||||
|
BIN
data/skins/forest/glass_square_focused_bw.png
Normal file
BIN
data/skins/forest/glass_square_focused_bw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
data/skins/ocean/glass_square_focused_bw.png
Normal file
BIN
data/skins/ocean/glass_square_focused_bw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
data/skins/peach/glass_square_focused_bw.png
Normal file
BIN
data/skins/peach/glass_square_focused_bw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
data/skins/ruby/glass_square_focused_bw.png
Normal file
BIN
data/skins/ruby/glass_square_focused_bw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 942 B |
@ -269,10 +269,14 @@ void STKConfig::getAllData(const XMLNode * root)
|
|||||||
|
|
||||||
if (const XMLNode *camera = root->getNode("camera"))
|
if (const XMLNode *camera = root->getNode("camera"))
|
||||||
{
|
{
|
||||||
camera->get("fov-1", &m_camera_fov[0]);
|
for (int i = 0; i < 4; i++)
|
||||||
camera->get("fov-2", &m_camera_fov[1]);
|
{
|
||||||
camera->get("fov-3", &m_camera_fov[2]);
|
camera->get("fov-" + std::to_string(i + 1), &m_camera_fov[i]);
|
||||||
camera->get("fov-4", &m_camera_fov[3]);
|
}
|
||||||
|
for (int i = 4; i < MAX_PLAYER_COUNT; i++)
|
||||||
|
{
|
||||||
|
camera->get("fov-" + std::to_string(4), &m_camera_fov[i]);
|
||||||
|
}
|
||||||
camera->get("cutscene-fov", &m_cutscene_fov);
|
camera->get("cutscene-fov", &m_cutscene_fov);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "network/remote_kart_info.hpp"
|
#include "network/remote_kart_info.hpp"
|
||||||
#include "utils/no_copy.hpp"
|
#include "utils/no_copy.hpp"
|
||||||
|
|
||||||
|
#include "utils/constants.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -148,7 +149,7 @@ public:
|
|||||||
float m_replay_delta_angle;
|
float m_replay_delta_angle;
|
||||||
|
|
||||||
/** The field of view for 1, 2, 3, 4 player split screen. */
|
/** The field of view for 1, 2, 3, 4 player split screen. */
|
||||||
float m_camera_fov[4];
|
float m_camera_fov[MAX_PLAYER_COUNT];
|
||||||
|
|
||||||
float m_cutscene_fov;
|
float m_cutscene_fov;
|
||||||
|
|
||||||
|
@ -561,7 +561,10 @@ namespace UserConfigParams
|
|||||||
PARAM_PREFIX BoolUserConfigParam m_force_legacy_device
|
PARAM_PREFIX BoolUserConfigParam m_force_legacy_device
|
||||||
PARAM_DEFAULT(BoolUserConfigParam(false, "force_legacy_device",
|
PARAM_DEFAULT(BoolUserConfigParam(false, "force_legacy_device",
|
||||||
&m_video_group, "Force OpenGL 2 context, even if OpenGL 3 is available."));
|
&m_video_group, "Force OpenGL 2 context, even if OpenGL 3 is available."));
|
||||||
|
PARAM_PREFIX BoolUserConfigParam split_screen_horizontally
|
||||||
|
PARAM_DEFAULT(BoolUserConfigParam(true, "split_screen_horizontally",
|
||||||
|
&m_video_group, "When playing a non-square amount of players (e.g. 2),"
|
||||||
|
" should it split horizontally (top/bottom)"));
|
||||||
PARAM_PREFIX BoolUserConfigParam m_texture_compression
|
PARAM_PREFIX BoolUserConfigParam m_texture_compression
|
||||||
PARAM_DEFAULT(BoolUserConfigParam(true, "enable_texture_compression",
|
PARAM_DEFAULT(BoolUserConfigParam(true, "enable_texture_compression",
|
||||||
&m_video_group, "Enable Texture Compression"));
|
&m_video_group, "Enable Texture Compression"));
|
||||||
@ -614,7 +617,6 @@ namespace UserConfigParams
|
|||||||
PARAM_DEFAULT(BoolUserConfigParam(true, "limit_game_fps",
|
PARAM_DEFAULT(BoolUserConfigParam(true, "limit_game_fps",
|
||||||
&m_recording_group, "Limit game framerate not beyond the fps of"
|
&m_recording_group, "Limit game framerate not beyond the fps of"
|
||||||
" recording video."));
|
" recording video."));
|
||||||
|
|
||||||
PARAM_PREFIX IntUserConfigParam m_video_format
|
PARAM_PREFIX IntUserConfigParam m_video_format
|
||||||
PARAM_DEFAULT(IntUserConfigParam(0, "video_format",
|
PARAM_DEFAULT(IntUserConfigParam(0, "video_format",
|
||||||
&m_recording_group, "Specify the video for record, which is the enum"
|
&m_recording_group, "Specify the video for record, which is the enum"
|
||||||
|
@ -163,71 +163,15 @@ void Camera::setKart(AbstractKart *new_kart)
|
|||||||
*/
|
*/
|
||||||
void Camera::setupCamera()
|
void Camera::setupCamera()
|
||||||
{
|
{
|
||||||
m_aspect = (float)(irr_driver->getActualScreenSize().Width)
|
m_viewport = irr_driver->GetSplitscreenWindow(m_index);
|
||||||
/ irr_driver->getActualScreenSize().Height;
|
m_aspect = (float)((float)(m_viewport.getWidth()) / (float)(m_viewport.getHeight()));
|
||||||
switch(race_manager->getNumLocalPlayers())
|
|
||||||
{
|
m_scaling = core::vector2df(
|
||||||
case 1: m_viewport = core::recti(0, 0,
|
irr_driver->getActualScreenSize().Width / m_viewport.getWidth() ,
|
||||||
irr_driver->getActualScreenSize().Width,
|
irr_driver->getActualScreenSize().Height / m_viewport.getHeight());
|
||||||
irr_driver->getActualScreenSize().Height);
|
|
||||||
m_scaling = core::vector2df(1.0f, 1.0f);
|
m_fov = DEGREE_TO_RAD * stk_config->m_camera_fov[race_manager->getNumLocalPlayers() - 1];
|
||||||
m_fov = DEGREE_TO_RAD*stk_config->m_camera_fov[0];
|
|
||||||
break;
|
|
||||||
case 2: m_viewport = core::recti(0,
|
|
||||||
m_index==0 ? 0
|
|
||||||
: irr_driver->getActualScreenSize().Height>>1,
|
|
||||||
irr_driver->getActualScreenSize().Width,
|
|
||||||
m_index==0 ? irr_driver->getActualScreenSize().Height>>1
|
|
||||||
: irr_driver->getActualScreenSize().Height);
|
|
||||||
m_scaling = core::vector2df(1.0f, 0.5f);
|
|
||||||
m_aspect *= 2.0f;
|
|
||||||
m_fov = DEGREE_TO_RAD*stk_config->m_camera_fov[1];
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/*
|
|
||||||
if(m_index<2)
|
|
||||||
{
|
|
||||||
m_viewport = core::recti(m_index==0 ? 0
|
|
||||||
: irr_driver->getActualScreenSize().Width>>1,
|
|
||||||
0,
|
|
||||||
m_index==0 ? irr_driver->getActualScreenSize().Width>>1
|
|
||||||
: irr_driver->getActualScreenSize().Width,
|
|
||||||
irr_driver->getActualScreenSize().Height>>1);
|
|
||||||
m_scaling = core::vector2df(0.5f, 0.5f);
|
|
||||||
m_fov = DEGREE_TO_RAD*50.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_viewport = core::recti(0, irr_driver->getActualScreenSize().Height>>1,
|
|
||||||
irr_driver->getActualScreenSize().Width,
|
|
||||||
irr_driver->getActualScreenSize().Height);
|
|
||||||
m_scaling = core::vector2df(1.0f, 0.5f);
|
|
||||||
m_fov = DEGREE_TO_RAD*65.0f;
|
|
||||||
m_aspect *= 2.0f;
|
|
||||||
}
|
|
||||||
break;*/
|
|
||||||
case 4:
|
|
||||||
{ // g++ 4.3 whines about the variables in switch/case if not {}-wrapped (???)
|
|
||||||
const int x1 = (m_index%2==0 ? 0 : irr_driver->getActualScreenSize().Width>>1);
|
|
||||||
const int y1 = (m_index<2 ? 0 : irr_driver->getActualScreenSize().Height>>1);
|
|
||||||
const int x2 = (m_index%2==0 ? irr_driver->getActualScreenSize().Width>>1 : irr_driver->getActualScreenSize().Width);
|
|
||||||
const int y2 = (m_index<2 ? irr_driver->getActualScreenSize().Height>>1 : irr_driver->getActualScreenSize().Height);
|
|
||||||
m_viewport = core::recti(x1, y1, x2, y2);
|
|
||||||
m_scaling = core::vector2df(0.5f, 0.5f);
|
|
||||||
m_fov = DEGREE_TO_RAD*stk_config->m_camera_fov[3];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if(UserConfigParams::logMisc())
|
|
||||||
Log::warn("Camera", "Incorrect number of players: '%d' - assuming 1.",
|
|
||||||
race_manager->getNumLocalPlayers());
|
|
||||||
m_viewport = core::recti(0, 0,
|
|
||||||
irr_driver->getActualScreenSize().Width,
|
|
||||||
irr_driver->getActualScreenSize().Height);
|
|
||||||
m_scaling = core::vector2df(1.0f, 1.0f);
|
|
||||||
m_fov = DEGREE_TO_RAD*75.0f;
|
|
||||||
break;
|
|
||||||
} // switch
|
|
||||||
m_camera->setFOV(m_fov);
|
m_camera->setFOV(m_fov);
|
||||||
m_camera->setAspectRatio(m_aspect);
|
m_camera->setAspectRatio(m_aspect);
|
||||||
m_camera->setFarValue(Track::getCurrentTrack()->getCameraFar());
|
m_camera->setFarValue(Track::getCurrentTrack()->getCameraFar());
|
||||||
|
@ -230,7 +230,31 @@ void IrrDriver::updateConfigIfRelevant()
|
|||||||
}
|
}
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
} // updateConfigIfRelevant
|
} // updateConfigIfRelevant
|
||||||
|
core::recti IrrDriver::GetSplitscreenWindow(int WindowNum)
|
||||||
|
{
|
||||||
|
const int playernum = race_manager->getNumLocalPlayers();
|
||||||
|
const float playernum_sqrt = sqrt(playernum);
|
||||||
|
|
||||||
|
int rows = UserConfigParams::split_screen_horizontally ? ceil(playernum_sqrt) : round(playernum_sqrt);
|
||||||
|
int cols = UserConfigParams::split_screen_horizontally ? round(playernum_sqrt) : ceil(playernum_sqrt);
|
||||||
|
|
||||||
|
if (rows == 0){rows = 1;}
|
||||||
|
if (cols == 0) {cols = 1;}
|
||||||
|
//This could add a bit of overhang
|
||||||
|
const int width_of_space = ceil((float)irr_driver->getActualScreenSize().Width / (float)cols);
|
||||||
|
const int height_of_space = ceil((float)irr_driver->getActualScreenSize().Height / (float)rows);
|
||||||
|
|
||||||
|
const int x_grid_Position = WindowNum % cols;
|
||||||
|
const int y_grid_Position = floor((WindowNum) / cols);
|
||||||
|
int wid = (int)irr_driver->getActualScreenSize().Width;
|
||||||
|
|
||||||
|
//To prevent the viewport going over the right side, we use std::min to ensure the right corners are never larger than the total width
|
||||||
|
return core::recti(
|
||||||
|
x_grid_Position * width_of_space,
|
||||||
|
y_grid_Position * height_of_space,
|
||||||
|
(x_grid_Position * width_of_space) + width_of_space,
|
||||||
|
(y_grid_Position * height_of_space) + height_of_space);
|
||||||
|
}
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Gets a list of supported video modes from the irrlicht device. This data
|
/** Gets a list of supported video modes from the irrlicht device. This data
|
||||||
* is stored in m_modes.
|
* is stored in m_modes.
|
||||||
|
@ -203,6 +203,7 @@ public:
|
|||||||
void increaseObjectCount();
|
void increaseObjectCount();
|
||||||
core::array<video::IRenderTarget> &getMainSetup();
|
core::array<video::IRenderTarget> &getMainSetup();
|
||||||
void updateConfigIfRelevant();
|
void updateConfigIfRelevant();
|
||||||
|
core::recti GetSplitscreenWindow(int WindowNum);
|
||||||
void setAllMaterialFlags(scene::IMesh *mesh) const;
|
void setAllMaterialFlags(scene::IMesh *mesh) const;
|
||||||
scene::IAnimatedMesh *getAnimatedMesh(const std::string &name);
|
scene::IAnimatedMesh *getAnimatedMesh(const std::string &name);
|
||||||
scene::IMesh *getMesh(const std::string &name);
|
scene::IMesh *getMesh(const std::string &name);
|
||||||
|
@ -912,6 +912,19 @@ void Skin::drawRibbon(const core::recti &rect, Widget* widget,
|
|||||||
{
|
{
|
||||||
} // drawRibbon
|
} // drawRibbon
|
||||||
|
|
||||||
|
SColorf GetPlayerColor(int player_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
SColorHSL col = { 0,100,50 };
|
||||||
|
col.Hue += (360 / 4) * (player_id % 4);
|
||||||
|
int color_id = player_id % 4;
|
||||||
|
SColorf color_rgb = { 0,0,0,1 };
|
||||||
|
|
||||||
|
|
||||||
|
col.Saturation = col.Saturation * (1.0F / (floor(player_id / 4) + 1) );
|
||||||
|
col.toRGB(color_rgb);
|
||||||
|
return color_rgb;
|
||||||
|
}
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* @param focused whether this element is focus by the master player (whether
|
* @param focused whether this element is focus by the master player (whether
|
||||||
@ -1069,8 +1082,8 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if multiple player selected the same ribbon item, we need to know
|
|
||||||
// to make it visible
|
//Handle drawing for the first player
|
||||||
int nPlayersOnThisItem = 0;
|
int nPlayersOnThisItem = 0;
|
||||||
|
|
||||||
if (mark_focused)
|
if (mark_focused)
|
||||||
@ -1134,11 +1147,24 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
|
|||||||
}
|
}
|
||||||
} // end if mark_focused
|
} // end if mark_focused
|
||||||
|
|
||||||
|
//Handle drawing for everyone else
|
||||||
|
for (int i = 1; i < MAX_PLAYER_COUNT; i++)
|
||||||
|
{
|
||||||
// ---- Draw selection for other players than player 1
|
// ---- Draw selection for other players than player 1
|
||||||
if (parentRibbon->isFocusedForPlayer(1) &&
|
if (parentRibbon->isFocusedForPlayer(i) &&
|
||||||
parentRibbon->getSelectionIDString(1) ==
|
parentRibbon->getSelectionIDString(i) ==
|
||||||
widget->m_properties[PROP_ID])
|
widget->m_properties[PROP_ID])
|
||||||
{
|
{
|
||||||
|
short red_previous = parentRibbonWidget->m_skin_r;
|
||||||
|
short green_previous = parentRibbonWidget->m_skin_g;
|
||||||
|
short blue_previous = parentRibbonWidget->m_skin_b;
|
||||||
|
|
||||||
|
SColorf color_rgb = GetPlayerColor(i);
|
||||||
|
|
||||||
|
parentRibbonWidget->m_skin_r = color_rgb.r * 255.0F;
|
||||||
|
parentRibbonWidget->m_skin_g = color_rgb.g * 255.0F;
|
||||||
|
parentRibbonWidget->m_skin_b = color_rgb.b * 255.0F;
|
||||||
|
|
||||||
if (nPlayersOnThisItem > 0)
|
if (nPlayersOnThisItem > 0)
|
||||||
{
|
{
|
||||||
core::recti rect2 = rect;
|
core::recti rect2 = rect;
|
||||||
@ -1147,62 +1173,20 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
|
|||||||
rect2.UpperLeftCorner.Y -= enlarge;
|
rect2.UpperLeftCorner.Y -= enlarge;
|
||||||
rect2.LowerRightCorner.X += enlarge;
|
rect2.LowerRightCorner.X += enlarge;
|
||||||
rect2.LowerRightCorner.Y += enlarge;
|
rect2.LowerRightCorner.Y += enlarge;
|
||||||
|
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect2,
|
drawBoxFromStretchableTexture(parentRibbonWidget, rect2,
|
||||||
SkinConfig::m_render_params["squareFocusHalo2::neutral"]);
|
SkinConfig::m_render_params["squareFocusHaloBW::neutral"]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
|
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
|
||||||
SkinConfig::m_render_params["squareFocusHalo2::neutral"]);
|
SkinConfig::m_render_params["squareFocusHaloBW::neutral"]);
|
||||||
}
|
}
|
||||||
|
parentRibbonWidget->m_skin_r = red_previous;
|
||||||
|
parentRibbonWidget->m_skin_g = green_previous;
|
||||||
|
parentRibbonWidget->m_skin_b = blue_previous;
|
||||||
nPlayersOnThisItem++;
|
nPlayersOnThisItem++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentRibbon->isFocusedForPlayer(2) &&
|
|
||||||
parentRibbon->getSelectionIDString(2) ==
|
|
||||||
widget->m_properties[PROP_ID])
|
|
||||||
{
|
|
||||||
if (nPlayersOnThisItem > 0)
|
|
||||||
{
|
|
||||||
core::recti rect2 = rect;
|
|
||||||
const int enlarge = nPlayersOnThisItem*6;
|
|
||||||
rect2.UpperLeftCorner.X -= enlarge;
|
|
||||||
rect2.UpperLeftCorner.Y -= enlarge;
|
|
||||||
rect2.LowerRightCorner.X += enlarge;
|
|
||||||
rect2.LowerRightCorner.Y += enlarge;
|
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect2,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo3::neutral"]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo3::neutral"]);
|
|
||||||
}
|
|
||||||
nPlayersOnThisItem++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentRibbon->isFocusedForPlayer(3) &&
|
|
||||||
parentRibbon->getSelectionIDString(3) ==
|
|
||||||
widget->m_properties[PROP_ID])
|
|
||||||
{
|
|
||||||
if (nPlayersOnThisItem > 0)
|
|
||||||
{
|
|
||||||
core::recti rect2 = rect;
|
|
||||||
const int enlarge = nPlayersOnThisItem*6;
|
|
||||||
rect2.UpperLeftCorner.X -= enlarge;
|
|
||||||
rect2.UpperLeftCorner.Y -= enlarge;
|
|
||||||
rect2.LowerRightCorner.X += enlarge;
|
|
||||||
rect2.LowerRightCorner.Y += enlarge;
|
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect2,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo4::neutral"]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo4::neutral"]);
|
|
||||||
}
|
|
||||||
nPlayersOnThisItem++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawIconButton(rect, widget, pressed, focused);
|
drawIconButton(rect, widget, pressed, focused);
|
||||||
@ -1224,6 +1208,7 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
|
|||||||
#endif
|
#endif
|
||||||
} // drawRibbonChild
|
} // drawRibbonChild
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* @param focused whether this element is focus by the master player (whether
|
* @param focused whether this element is focus by the master player (whether
|
||||||
@ -1256,25 +1241,28 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BoxRenderParams* params;
|
BoxRenderParams* params;
|
||||||
SpinnerWidget* q = dynamic_cast<SpinnerWidget*>(widget);
|
SpinnerWidget* q = dynamic_cast<SpinnerWidget*>(widget);
|
||||||
if(q->getUseBackgroundColor())
|
if(q->getUseBackgroundColor())
|
||||||
{
|
{
|
||||||
int player_id=q->getSpinnerWidgetPlayerID();
|
int player_id=q->getSpinnerWidgetPlayerID();
|
||||||
|
|
||||||
|
params = &SkinConfig::m_render_params[
|
||||||
|
"spinner::deactivated"];
|
||||||
|
|
||||||
|
SColorf color_rgb = GetPlayerColor(player_id);
|
||||||
|
|
||||||
|
widget->m_skin_r = color_rgb.r * 255.0F;
|
||||||
|
widget->m_skin_g = color_rgb.g * 255.0F;
|
||||||
|
widget->m_skin_b = color_rgb.b * 255.0F;
|
||||||
|
|
||||||
if (player_id == 0)
|
if (player_id == 0)
|
||||||
params=&SkinConfig::m_render_params["spinner1::neutral"];
|
|
||||||
else if(player_id==1)
|
|
||||||
params=&SkinConfig::m_render_params["spinner2::neutral"];
|
|
||||||
else if(player_id==2)
|
|
||||||
params=&SkinConfig::m_render_params["spinner3::neutral"];
|
|
||||||
else if(player_id==3)
|
|
||||||
params=&SkinConfig::m_render_params["spinner4::neutral"];
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Log::fatal("Skin::drawSpinnerBody", "Unknown playerID (more than 4 players?)");
|
color_rgb = { 1,1,1,1 };
|
||||||
// Silence compiler warning
|
|
||||||
params = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (widget->m_deactivated)
|
else if (widget->m_deactivated)
|
||||||
{
|
{
|
||||||
@ -1288,49 +1276,24 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget,
|
|||||||
{
|
{
|
||||||
params=&SkinConfig::m_render_params["spinner::neutral"];
|
params=&SkinConfig::m_render_params["spinner::neutral"];
|
||||||
}
|
}
|
||||||
if (widget->isFocusedForPlayer(0))
|
for (int i = 1; i < MAX_PLAYER_COUNT + 1; i++)
|
||||||
{
|
{
|
||||||
|
if (widget->isFocusedForPlayer(i - 1)) {
|
||||||
core::recti rect2 = rect;
|
core::recti rect2 = rect;
|
||||||
rect2.UpperLeftCorner.X += 2;
|
rect2.UpperLeftCorner.X += 2;
|
||||||
rect2.UpperLeftCorner.Y -= 3;
|
rect2.UpperLeftCorner.Y -= 3;
|
||||||
rect2.LowerRightCorner.X -= 2;
|
rect2.LowerRightCorner.X -= 2;
|
||||||
rect2.LowerRightCorner.Y += 5;
|
rect2.LowerRightCorner.Y += 5;
|
||||||
drawBoxFromStretchableTexture(widget, rect2,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo::neutral"]);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (widget->isFocusedForPlayer(1))
|
|
||||||
{
|
|
||||||
core::recti rect2 = rect;
|
|
||||||
rect2.UpperLeftCorner.X += 2;
|
|
||||||
rect2.UpperLeftCorner.Y -= 3;
|
|
||||||
rect2.LowerRightCorner.X -= 2;
|
|
||||||
rect2.LowerRightCorner.Y += 5;
|
|
||||||
drawBoxFromStretchableTexture(widget, rect2,
|
drawBoxFromStretchableTexture(widget, rect2,
|
||||||
SkinConfig::m_render_params["squareFocusHalo2::neutral"]);
|
SkinConfig::m_render_params["squareFocusHaloBW::neutral"]);
|
||||||
|
//TODO add squarefocushalo0
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (widget->isFocusedForPlayer(2))
|
|
||||||
{
|
|
||||||
core::recti rect2 = rect;
|
|
||||||
rect2.UpperLeftCorner.X += 2;
|
|
||||||
rect2.UpperLeftCorner.Y -= 3;
|
|
||||||
rect2.LowerRightCorner.X -= 2;
|
|
||||||
rect2.LowerRightCorner.Y += 5;
|
|
||||||
drawBoxFromStretchableTexture(widget, rect2,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo3::neutral"]);
|
|
||||||
}
|
|
||||||
else if (widget->isFocusedForPlayer(3))
|
|
||||||
{
|
|
||||||
core::recti rect2 = rect;
|
|
||||||
rect2.UpperLeftCorner.X += 2;
|
|
||||||
rect2.UpperLeftCorner.Y -= 3;
|
|
||||||
rect2.LowerRightCorner.X -= 2;
|
|
||||||
rect2.LowerRightCorner.Y += 5;
|
|
||||||
drawBoxFromStretchableTexture(widget, rect2,
|
|
||||||
SkinConfig::m_render_params["squareFocusHalo4::neutral"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
core::recti sized_rect = rect;
|
core::recti sized_rect = rect;
|
||||||
if (m_dialog && m_dialog_size < 1.0f && widget->m_parent != NULL &&
|
if (m_dialog && m_dialog_size < 1.0f && widget->m_parent != NULL &&
|
||||||
widget->m_parent->getType() == gui::EGUIET_WINDOW)
|
widget->m_parent->getType() == gui::EGUIET_WINDOW)
|
||||||
|
@ -489,6 +489,16 @@ public:
|
|||||||
{
|
{
|
||||||
return m_num_local_players;
|
return m_num_local_players;
|
||||||
} // getNumLocalPlayers
|
} // getNumLocalPlayers
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool getIfEmptyScreenSpaceExists() const
|
||||||
|
{
|
||||||
|
const float Sqrt = sqrt(getNumLocalPlayers());
|
||||||
|
const int rows = ceil(Sqrt);
|
||||||
|
const int cols = round(Sqrt);
|
||||||
|
const int total_spaces = rows * cols;
|
||||||
|
return (total_spaces - getNumLocalPlayers() > 0);
|
||||||
|
} // getNumLocalPlayers
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
/** Returns the selected number of karts (selected number of players and
|
/** Returns the selected number of karts (selected number of players and
|
||||||
* AI karts. */
|
* AI karts. */
|
||||||
|
@ -128,6 +128,13 @@ void OptionsScreenUI::init()
|
|||||||
assert( skinSelector != NULL );
|
assert( skinSelector != NULL );
|
||||||
|
|
||||||
// ---- video modes
|
// ---- video modes
|
||||||
|
CheckBoxWidget* splitscreen_method = getWidget<CheckBoxWidget>("split_screen_horizontally");
|
||||||
|
assert(splitscreen_method != NULL);
|
||||||
|
splitscreen_method->setState(UserConfigParams::split_screen_horizontally);
|
||||||
|
|
||||||
|
//Forbid changing this setting in game
|
||||||
|
bool in_game = StateManager::get()->getGameState() == GUIEngine::INGAME_MENU;
|
||||||
|
splitscreen_method->setActive(!in_game);
|
||||||
|
|
||||||
CheckBoxWidget* fps = getWidget<CheckBoxWidget>("showfps");
|
CheckBoxWidget* fps = getWidget<CheckBoxWidget>("showfps");
|
||||||
assert( fps != NULL );
|
assert( fps != NULL );
|
||||||
@ -221,6 +228,7 @@ void OptionsScreenUI::init()
|
|||||||
// tearing down and rebuilding the menu stack. not good when in-game)
|
// tearing down and rebuilding the menu stack. not good when in-game)
|
||||||
list_widget->setActive(StateManager::get()->getGameState() != GUIEngine::INGAME_MENU);
|
list_widget->setActive(StateManager::get()->getGameState() != GUIEngine::INGAME_MENU);
|
||||||
|
|
||||||
|
|
||||||
} // init
|
} // init
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -258,6 +266,13 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con
|
|||||||
UserConfigParams::m_skin_file = core::stringc(selectedSkin.c_str()).c_str() + std::string(".stkskin");
|
UserConfigParams::m_skin_file = core::stringc(selectedSkin.c_str()).c_str() + std::string(".stkskin");
|
||||||
GUIEngine::reloadSkin();
|
GUIEngine::reloadSkin();
|
||||||
}
|
}
|
||||||
|
else if (name == "split_screen_horizontally")
|
||||||
|
{
|
||||||
|
CheckBoxWidget* split_screen_horizontally = getWidget<CheckBoxWidget>("split_screen_horizontally");
|
||||||
|
assert(split_screen_horizontally != NULL);
|
||||||
|
UserConfigParams::split_screen_horizontally = split_screen_horizontally->getState();
|
||||||
|
|
||||||
|
}
|
||||||
else if (name == "showfps")
|
else if (name == "showfps")
|
||||||
{
|
{
|
||||||
CheckBoxWidget* fps = getWidget<CheckBoxWidget>("showfps");
|
CheckBoxWidget* fps = getWidget<CheckBoxWidget>("showfps");
|
||||||
|
@ -121,7 +121,7 @@ RaceGUI::RaceGUI()
|
|||||||
|
|
||||||
|
|
||||||
// special case : when 3 players play, use available 4th space for such things
|
// special case : when 3 players play, use available 4th space for such things
|
||||||
if (race_manager->getNumLocalPlayers() == 3)
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
||||||
{
|
{
|
||||||
m_map_left = irr_driver->getActualScreenSize().Width - m_map_width;
|
m_map_left = irr_driver->getActualScreenSize().Width - m_map_width;
|
||||||
}
|
}
|
||||||
@ -190,15 +190,13 @@ void RaceGUI::renderGlobal(float dt)
|
|||||||
|
|
||||||
// Special case : when 3 players play, use 4th window to display such
|
// Special case : when 3 players play, use 4th window to display such
|
||||||
// stuff (but we must clear it)
|
// stuff (but we must clear it)
|
||||||
if (race_manager->getNumLocalPlayers() == 3 &&
|
if (race_manager->getIfEmptyScreenSpaceExists() &&
|
||||||
!GUIEngine::ModalDialog::isADialogActive())
|
!GUIEngine::ModalDialog::isADialogActive())
|
||||||
{
|
{
|
||||||
static video::SColor black = video::SColor(255,0,0,0);
|
static video::SColor black = video::SColor(255,0,0,0);
|
||||||
GL32_draw2DRectangle(black,
|
|
||||||
core::rect<s32>(irr_driver->getActualScreenSize().Width/2,
|
GL32_draw2DRectangle(black, irr_driver->GetSplitscreenWindow(
|
||||||
irr_driver->getActualScreenSize().Height/2,
|
race_manager->getNumLocalPlayers()));
|
||||||
irr_driver->getActualScreenSize().Width,
|
|
||||||
irr_driver->getActualScreenSize().Height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
World *world = World::getWorld();
|
World *world = World::getWorld();
|
||||||
@ -255,7 +253,7 @@ void RaceGUI::renderPlayerView(const Camera *camera, float dt)
|
|||||||
|
|
||||||
drawPlungerInFace(camera, dt);
|
drawPlungerInFace(camera, dt);
|
||||||
|
|
||||||
scaling *= viewport.getWidth()/800.0f; // scale race GUI along screen size
|
scaling *= float(viewport.getWidth()) / float(irr_driver->getActualScreenSize().Width); // scale race GUI along screen size
|
||||||
drawAllMessages(kart, viewport, scaling);
|
drawAllMessages(kart, viewport, scaling);
|
||||||
|
|
||||||
if(!World::getWorld()->isRacePhase()) return;
|
if(!World::getWorld()->isRacePhase()) return;
|
||||||
@ -368,9 +366,10 @@ void RaceGUI::drawGlobalTimer()
|
|||||||
irr_driver->getActualScreenSize().Width , 50);
|
irr_driver->getActualScreenSize().Width , 50);
|
||||||
|
|
||||||
// special case : when 3 players play, use available 4th space for such things
|
// special case : when 3 players play, use available 4th space for such things
|
||||||
if (race_manager->getNumLocalPlayers() == 3)
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
||||||
{
|
{
|
||||||
pos += core::vector2d<s32>(0, irr_driver->getActualScreenSize().Height/2);
|
pos -= core::vector2d<s32>(0, pos.LowerRightCorner.Y / 2);
|
||||||
|
pos += core::vector2d<s32>(0, irr_driver->getActualScreenSize().Height - irr_driver->GetSplitscreenWindow(0).getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
gui::ScalableFont* font = (use_digit_font ? GUIEngine::getHighresDigitFont() : GUIEngine::getFont());
|
gui::ScalableFont* font = (use_digit_font ? GUIEngine::getHighresDigitFont() : GUIEngine::getFont());
|
||||||
@ -888,14 +887,20 @@ void RaceGUI::drawLap(const AbstractKart* kart,
|
|||||||
if (lap < 0 ) return;
|
if (lap < 0 ) return;
|
||||||
|
|
||||||
core::recti pos;
|
core::recti pos;
|
||||||
pos.UpperLeftCorner.Y = viewport.UpperLeftCorner.Y + m_font_height;
|
|
||||||
// If the time display in the top right is in this viewport,
|
// If the time display in the top right is in this viewport,
|
||||||
// move the lap/rank display down a little bit so that it is
|
// move the lap/rank display down a little bit so that it is
|
||||||
// displayed under the time.
|
// displayed under the time.
|
||||||
if (viewport.UpperLeftCorner.Y == 0 &&
|
if (viewport.UpperLeftCorner.Y == 0 &&
|
||||||
viewport.LowerRightCorner.X == (int)(irr_driver->getActualScreenSize().Width) &&
|
viewport.LowerRightCorner.X == (int)(irr_driver->getActualScreenSize().Width) &&
|
||||||
race_manager->getNumPlayers()!=3)
|
!race_manager->getIfEmptyScreenSpaceExists())
|
||||||
|
{
|
||||||
pos.UpperLeftCorner.Y += m_font_height;
|
pos.UpperLeftCorner.Y += m_font_height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos.UpperLeftCorner.Y = viewport.UpperLeftCorner.Y + m_font_height;
|
||||||
|
}
|
||||||
pos.LowerRightCorner.Y = viewport.LowerRightCorner.Y+20;
|
pos.LowerRightCorner.Y = viewport.LowerRightCorner.Y+20;
|
||||||
pos.UpperLeftCorner.X = viewport.LowerRightCorner.X
|
pos.UpperLeftCorner.X = viewport.LowerRightCorner.X
|
||||||
- m_lap_width - 10;
|
- m_lap_width - 10;
|
||||||
|
@ -207,8 +207,8 @@ void RaceGUIBase::drawAllMessages(const AbstractKart* kart,
|
|||||||
{
|
{
|
||||||
int y = viewport.LowerRightCorner.Y - m_small_font_max_height - 10;
|
int y = viewport.LowerRightCorner.Y - m_small_font_max_height - 10;
|
||||||
|
|
||||||
const int x = (viewport.LowerRightCorner.X + viewport.UpperLeftCorner.X)/2;
|
const int x = viewport.getCenter().X;
|
||||||
const int w = (viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X);
|
const int w = viewport.getWidth();
|
||||||
|
|
||||||
// Draw less important messages first, at the very bottom of the screen
|
// Draw less important messages first, at the very bottom of the screen
|
||||||
// unimportant messages are skipped in multiplayer, they take too much screen space
|
// unimportant messages are skipped in multiplayer, they take too much screen space
|
||||||
@ -244,7 +244,7 @@ void RaceGUIBase::drawAllMessages(const AbstractKart* kart,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First line of text somewhat under the top of the viewport.
|
// First line of text somewhat under the top of the viewport.
|
||||||
y = (int)(viewport.UpperLeftCorner.Y + 164*scaling.Y);
|
y = (int)(viewport.UpperLeftCorner.Y + 20);
|
||||||
|
|
||||||
gui::ScalableFont* font = GUIEngine::getFont();
|
gui::ScalableFont* font = GUIEngine::getFont();
|
||||||
gui::ScalableFont* big_font = GUIEngine::getTitleFont();
|
gui::ScalableFont* big_font = GUIEngine::getTitleFont();
|
||||||
@ -665,10 +665,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
|||||||
int y_base = 20;
|
int y_base = 20;
|
||||||
unsigned int y_space = irr_driver->getActualScreenSize().Height - bottom_margin - y_base;
|
unsigned int y_space = irr_driver->getActualScreenSize().Height - bottom_margin - y_base;
|
||||||
// Special case : when 3 players play, use 4th window to display such stuff
|
// Special case : when 3 players play, use 4th window to display such stuff
|
||||||
if (race_manager->getNumLocalPlayers() == 3)
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
||||||
{
|
{
|
||||||
x_base = irr_driver->getActualScreenSize().Width/2 + x_base;
|
irr::core::recti Last_Space = irr_driver->GetSplitscreenWindow(race_manager->getNumLocalPlayers());
|
||||||
y_base = irr_driver->getActualScreenSize().Height/2 + y_base;
|
x_base = Last_Space.UpperLeftCorner.X;
|
||||||
|
y_base = Last_Space.UpperLeftCorner.Y;
|
||||||
y_space = irr_driver->getActualScreenSize().Height - y_base;
|
y_space = irr_driver->getActualScreenSize().Height - y_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,7 +730,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
|||||||
//where is the limit to hide last icons
|
//where is the limit to hide last icons
|
||||||
int y_icons_limit = irr_driver->getActualScreenSize().Height -
|
int y_icons_limit = irr_driver->getActualScreenSize().Height -
|
||||||
bottom_margin - ICON_PLAYER_WIDTH;
|
bottom_margin - ICON_PLAYER_WIDTH;
|
||||||
if (race_manager->getNumLocalPlayers() == 3)
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
||||||
{
|
{
|
||||||
y_icons_limit = irr_driver->getActualScreenSize().Height - ICON_WIDTH;
|
y_icons_limit = irr_driver->getActualScreenSize().Height - ICON_WIDTH;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ RaceGUIOverworld::RaceGUIOverworld()
|
|||||||
|
|
||||||
|
|
||||||
// special case : when 3 players play, use available 4th space for such things
|
// special case : when 3 players play, use available 4th space for such things
|
||||||
if (race_manager->getNumLocalPlayers() == 3)
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
||||||
{
|
{
|
||||||
m_map_left = irr_driver->getActualScreenSize().Width - m_map_width;
|
m_map_left = irr_driver->getActualScreenSize().Width - m_map_width;
|
||||||
}
|
}
|
||||||
@ -179,15 +179,15 @@ void RaceGUIOverworld::renderGlobal(float dt)
|
|||||||
|
|
||||||
// Special case : when 3 players play, use 4th window to display such
|
// Special case : when 3 players play, use 4th window to display such
|
||||||
// stuff (but we must clear it)
|
// stuff (but we must clear it)
|
||||||
if (race_manager->getNumLocalPlayers() == 3 &&
|
if (race_manager->getIfEmptyScreenSpaceExists() &&
|
||||||
!GUIEngine::ModalDialog::isADialogActive())
|
!GUIEngine::ModalDialog::isADialogActive())
|
||||||
{
|
{
|
||||||
|
const float Sqrt = sqrt(race_manager->getNumLocalPlayers());
|
||||||
|
const int rows = ceil(Sqrt);
|
||||||
|
const int cols = round(Sqrt);
|
||||||
static video::SColor black = video::SColor(255,0,0,0);
|
static video::SColor black = video::SColor(255,0,0,0);
|
||||||
GL32_draw2DRectangle(black,
|
GL32_draw2DRectangle(black, irr_driver->GetSplitscreenWindow(
|
||||||
core::rect<s32>(irr_driver->getActualScreenSize().Width/2,
|
race_manager->getNumLocalPlayers()));
|
||||||
irr_driver->getActualScreenSize().Height/2,
|
|
||||||
irr_driver->getActualScreenSize().Width,
|
|
||||||
irr_driver->getActualScreenSize().Height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
World *world = World::getWorld();
|
World *world = World::getWorld();
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
#define DEGREE_TO_RAD (M_PI/180.0f)
|
#define DEGREE_TO_RAD (M_PI/180.0f)
|
||||||
#define RAD_TO_DEGREE (180.0f/M_PI)
|
#define RAD_TO_DEGREE (180.0f/M_PI)
|
||||||
|
|
||||||
const unsigned int MAX_PLAYER_COUNT = 4;
|
const unsigned int MAX_PLAYER_COUNT = 8;
|
||||||
|
|
||||||
extern const bool IS_LITTLE_ENDIAN;
|
extern const bool IS_LITTLE_ENDIAN;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user