mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #5085
5085: New DDRace HUD r=def- a=C0D3D3V If you want to test this PR, you have to test it on a Server that includes this PR too. Textures are made by Ravie Here a showcase video with most parts shown: https://youtu.be/gPTVj-s3pgc Added to the new HUD - A display of the weapons available to the player - The weapon the player is carrying is highlighted - Indicators for the special abilities of the player (Endless Hook, Endless Jumps, Jetpack, Teleport Weapons) - Indicators for abilities taken away from the player (Deep/Life Freeze, No Hook, No Weapons collision, No Collision) - Control indicators for dummy controls (dummy hammer, dummy copy) (bottom right) - Jump indicator (max 10 jumps ar displayed, and greyed out as soon as a jump is used) - Ninja status bar that indicates how long a player is capable of using ninja (next to the ninja sword) - Freeze status bar that indicates the thawing time of a player (below player) - Movement Information can be displayed in a clean way above the mini score HUD (Position, Speed, Target Angle) - Indicator if you are in practice mode The complete HUD also works for players you spectate I Added a new NetObj since the predicted values are not perfect and would make the display of the information a lot less good: DDNetCharacterDisplayInfo that contains the following information ``` NetIntRange("m_JumpedTotal", -2, 255), NetTick("m_NinjaActivationTick"), NetTick("m_FreezeTick"), NetBool("m_IsInFreeze"), NetBool("m_IsInPracticeMode"), NetIntAny("m_TargetX"), # used for the Movement Information display NetIntAny("m_TargetY"), NetIntAny("m_RampValue"), ``` So if someone has an idea what data we could also need in the client for making the display more nice, now is the right moment to add more data to this network object. A few screenshots: Assets Tab: ![grafik](https://user-images.githubusercontent.com/14315968/167703792-f0fa86be-159d-4e11-baf4-9539cee38aae.png) HUD Settings: ![grafik](https://user-images.githubusercontent.com/14315968/167704336-dc7a314e-5603-40a2-98b4-9c03377906dd.png) Mini Debug HUD: ![grafik](https://user-images.githubusercontent.com/14315968/168302791-c377d93e-33a2-4eb2-9d8d-b78f0808a009.png) Speed.X is calculated using the players ramp vaule ## Checklist - [x] Tested the change ingame - [x] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test if it works standalone, system.c especially - [x] Considered possible null pointers and out of bounds array indexing - [x] Changed no physics that affect existing maps - [x] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: c0d3d3v <c0d3d3v@mag-keinen-spam.de> Co-authored-by: Jupeyy <jupjopjap@gmail.com>
This commit is contained in:
commit
47874fb57f
|
@ -1234,6 +1234,7 @@ set(EXPECTED_DATA
|
|||
gui_cursor.png
|
||||
gui_icons.png
|
||||
gui_logo.png
|
||||
hud.png
|
||||
languages/arabic.txt
|
||||
languages/belarusian.txt
|
||||
languages/bosnian.txt
|
||||
|
@ -1964,6 +1965,8 @@ if(CLIENT)
|
|||
components/emoticon.h
|
||||
components/flow.cpp
|
||||
components/flow.h
|
||||
components/freezebars.cpp
|
||||
components/freezebars.h
|
||||
components/ghost.cpp
|
||||
components/ghost.h
|
||||
components/hud.cpp
|
||||
|
|
BIN
data/hud.png
Normal file
BIN
data/hud.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
|
@ -238,6 +238,7 @@ image_arrow = Image("arrow", "arrow.png")
|
|||
image_demobuttons2 = Image("demobuttons2", "demo_buttons2.png")
|
||||
image_audio_source = Image("audio_source", "editor/audio_source.png")
|
||||
image_strongweak = Image("strongweak", "strong_weak.png")
|
||||
image_hud = Image("hud", "hud.png")
|
||||
|
||||
container.images.Add(image_null)
|
||||
container.images.Add(image_game)
|
||||
|
@ -256,6 +257,7 @@ container.images.Add(image_arrow)
|
|||
container.images.Add(image_demobuttons2)
|
||||
container.images.Add(image_audio_source)
|
||||
container.images.Add(image_strongweak)
|
||||
container.images.Add(image_hud)
|
||||
|
||||
container.pickups.Add(Pickup("health"))
|
||||
container.pickups.Add(Pickup("armor"))
|
||||
|
@ -278,6 +280,7 @@ set_guiicons = SpriteSet("guiicons", image_guiicons, 12, 2)
|
|||
set_demobuttons2 = SpriteSet("demobuttons2", image_demobuttons2, 4, 1)
|
||||
set_audio_source = SpriteSet("audio_source", image_audio_source, 1, 1)
|
||||
set_strongweak = SpriteSet("strongweak", image_strongweak, 2, 1)
|
||||
set_hud = SpriteSet("hud", image_hud, 14, 12)
|
||||
|
||||
container.spritesets.Add(set_particles)
|
||||
container.spritesets.Add(set_game)
|
||||
|
@ -291,6 +294,7 @@ container.spritesets.Add(set_guiicons)
|
|||
container.spritesets.Add(set_demobuttons2)
|
||||
container.spritesets.Add(set_audio_source)
|
||||
container.spritesets.Add(set_strongweak)
|
||||
container.spritesets.Add(set_hud)
|
||||
|
||||
container.sprites.Add(Sprite("part_slice", set_particles, 0,0,1,1))
|
||||
container.sprites.Add(Sprite("part_ball", set_particles, 1,0,1,1))
|
||||
|
@ -362,12 +366,16 @@ container.sprites.Add(Sprite("weapon_ninja_muzzle3", set_game, 25,8,7,4))
|
|||
|
||||
container.sprites.Add(Sprite("pickup_health", set_game, 10,2,2,2))
|
||||
container.sprites.Add(Sprite("pickup_armor", set_game, 12,2,2,2))
|
||||
container.sprites.Add(Sprite("pickup_hammer", set_game, 2,1,4,3))
|
||||
container.sprites.Add(Sprite("pickup_gun", set_game, 2,4,4,2))
|
||||
container.sprites.Add(Sprite("pickup_shotgun", set_game, 2,6,8,2))
|
||||
container.sprites.Add(Sprite("pickup_grenade", set_game, 2,8,7,2))
|
||||
container.sprites.Add(Sprite("pickup_laser", set_game, 2,12,7,3))
|
||||
container.sprites.Add(Sprite("pickup_ninja", set_game, 2,10,8,2))
|
||||
container.sprites.Add(Sprite("pickup_armor_shotgun", set_game, 15,2,2,2))
|
||||
container.sprites.Add(Sprite("pickup_armor_grenade", set_game, 17,2,2,2))
|
||||
container.sprites.Add(Sprite("pickup_armor_laser", set_game, 19,2,2,2))
|
||||
container.sprites.Add(Sprite("pickup_armor_ninja", set_game, 10,10,2,2))
|
||||
container.sprites.Add(Sprite("pickup_weapon", set_game, 3,0,6,2))
|
||||
container.sprites.Add(Sprite("pickup_ninja", set_game, 2,10,8,2))
|
||||
container.sprites.Add(Sprite("pickup_armor_laser", set_game, 19,2,2,2))
|
||||
|
||||
container.sprites.Add(Sprite("flag_blue", set_game, 12,8,4,8))
|
||||
container.sprites.Add(Sprite("flag_red", set_game, 16,8,4,8))
|
||||
|
@ -434,6 +442,35 @@ container.sprites.Add(Sprite("audio_source", set_audio_source, 0,0,1,1))
|
|||
container.sprites.Add(Sprite("hook_strong", set_strongweak, 0,0,1,1))
|
||||
container.sprites.Add(Sprite("hook_weak", set_strongweak, 1,0,1,1))
|
||||
|
||||
container.sprites.Add(Sprite("hud_airjump", set_hud, 0,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_airjump_empty", set_hud, 2,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_solo", set_hud, 4,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_no_collision", set_hud, 6,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_endless_jump", set_hud, 8,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_endless_hook", set_hud, 10,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_jetpack", set_hud, 12,0,2,2))
|
||||
container.sprites.Add(Sprite("hud_freeze_bar_full_left", set_hud, 0,2,1,1))
|
||||
container.sprites.Add(Sprite("hud_freeze_bar_full", set_hud, 1,2,1,1))
|
||||
container.sprites.Add(Sprite("hud_freeze_bar_empty", set_hud, 2,2,1,1))
|
||||
container.sprites.Add(Sprite("hud_freeze_bar_empty_right", set_hud, 3,2,1,1))
|
||||
container.sprites.Add(Sprite("hud_ninja_bar_full_left", set_hud, 0,3,1,1))
|
||||
container.sprites.Add(Sprite("hud_ninja_bar_full", set_hud, 1,3,1,1))
|
||||
container.sprites.Add(Sprite("hud_ninja_bar_empty", set_hud, 2,3,1,1))
|
||||
container.sprites.Add(Sprite("hud_ninja_bar_empty_right", set_hud, 3,3,1,1))
|
||||
container.sprites.Add(Sprite("hud_no_hook_hit", set_hud, 4,2,2,2))
|
||||
container.sprites.Add(Sprite("hud_no_hammer_hit", set_hud, 6,2,2,2))
|
||||
container.sprites.Add(Sprite("hud_no_shotgun_hit", set_hud, 8,2,2,2))
|
||||
container.sprites.Add(Sprite("hud_no_grenade_hit", set_hud, 10,2,2,2))
|
||||
container.sprites.Add(Sprite("hud_no_laser_hit", set_hud, 12,2,2,2))
|
||||
container.sprites.Add(Sprite("hud_deep_frozen", set_hud, 10,4,2,2))
|
||||
container.sprites.Add(Sprite("hud_live_frozen", set_hud, 12,4,2,2))
|
||||
container.sprites.Add(Sprite("hud_teleport_grenade", set_hud, 4,4,2,2))
|
||||
container.sprites.Add(Sprite("hud_teleport_gun", set_hud, 6,4,2,2))
|
||||
container.sprites.Add(Sprite("hud_teleport_laser", set_hud, 8,4,2,2))
|
||||
container.sprites.Add(Sprite("hud_practice_mode", set_hud, 4,6,2,2))
|
||||
container.sprites.Add(Sprite("hud_dummy_hammer", set_hud, 6,6,2,2))
|
||||
container.sprites.Add(Sprite("hud_dummy_copy", set_hud, 8,6,2,2))
|
||||
|
||||
anim = Animation("base")
|
||||
anim.body.frames.Add(AnimKeyframe(0, 0, -4, 0))
|
||||
anim.back_foot.frames.Add(AnimKeyframe(0, 0, 10, 0))
|
||||
|
|
|
@ -243,6 +243,17 @@ Objects = [
|
|||
NetIntRange("m_StrongWeakID", 0, 'MAX_CLIENTS-1'),
|
||||
]),
|
||||
|
||||
NetObjectEx("DDNetCharacterDisplayInfo", "character-display-info@netobj.ddnet.tw", [
|
||||
NetIntRange("m_JumpedTotal", 0, 255),
|
||||
NetTick("m_NinjaActivationTick"),
|
||||
NetTick("m_FreezeTick"),
|
||||
NetBool("m_IsInFreeze"),
|
||||
NetBool("m_IsInPracticeMode"),
|
||||
NetIntAny("m_TargetX"),
|
||||
NetIntAny("m_TargetY"),
|
||||
NetIntAny("m_RampValue"),
|
||||
]),
|
||||
|
||||
NetObjectEx("DDNetPlayer", "player@netobj.ddnet.tw", [
|
||||
NetIntAny("m_Flags"),
|
||||
NetIntRange("m_AuthLevel", "AUTHED_NO", "AUTHED_ADMIN"),
|
||||
|
|
|
@ -129,6 +129,19 @@ void CGraph::Add(float v, float r, float g, float b)
|
|||
m_aColors[m_Index][2] = b;
|
||||
}
|
||||
|
||||
bool CGraph::InsertAt(int i, float v, float r, float g, float b)
|
||||
{
|
||||
if(i < 0 || i > MAX_VALUES - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_aValues[i] = v;
|
||||
m_aColors[i][0] = r;
|
||||
m_aColors[i][1] = g;
|
||||
m_aColors[i][2] = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGraph::Render(IGraphics *pGraphics, IGraphics::CTextureHandle FontTexture, float x, float y, float w, float h, const char *pDescription)
|
||||
{
|
||||
//m_pGraphics->BlendNormal();
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
void ScaleMin();
|
||||
|
||||
void Add(float v, float r, float g, float b);
|
||||
bool InsertAt(int i, float v, float r, float g, float b);
|
||||
void Render(IGraphics *pGraphics, IGraphics::CTextureHandle FontTexture, float x, float y, float w, float h, const char *pDescription);
|
||||
};
|
||||
|
||||
|
|
|
@ -162,7 +162,9 @@ static void Mix(short *pFinalOut, unsigned Frames)
|
|||
float r = Voice.m_Circle.m_Radius;
|
||||
RangeX = r;
|
||||
|
||||
int Dist = (int)sqrtf((float)dx * dx + (float)dy * dy); // nasty float
|
||||
// dx and dy can be larger than 46341 and thus the calculation would go beyond the limits of a integer,
|
||||
// therefore we cast them into float
|
||||
int Dist = (int)sqrtf((float)dx * dx + (float)dy * dy);
|
||||
if(Dist < r)
|
||||
{
|
||||
InVoiceField = true;
|
||||
|
|
|
@ -41,6 +41,7 @@ MACRO_CONFIG_STR(ClAssetsEntites, cl_assets_entities, 50, "default", CFGFLAG_SAV
|
|||
MACRO_CONFIG_STR(ClAssetGame, cl_asset_game, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for game")
|
||||
MACRO_CONFIG_STR(ClAssetEmoticons, cl_asset_emoticons, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for emoticons")
|
||||
MACRO_CONFIG_STR(ClAssetParticles, cl_asset_particles, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for particles")
|
||||
MACRO_CONFIG_STR(ClAssetHud, cl_asset_hud, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for HUD")
|
||||
|
||||
MACRO_CONFIG_STR(BrFilterString, br_filter_string, 25, "Novice", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Server browser filtering string")
|
||||
MACRO_CONFIG_STR(BrExcludeString, br_exclude_string, 25, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Server browser exclusion string")
|
||||
|
@ -304,7 +305,10 @@ MACRO_CONFIG_INT(ClRaceGhost, cl_race_ghost, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_S
|
|||
MACRO_CONFIG_INT(ClRaceGhostServerControl, cl_race_ghost_server_control, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Let the server start the ghost")
|
||||
MACRO_CONFIG_INT(ClRaceShowGhost, cl_race_show_ghost, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ghost")
|
||||
MACRO_CONFIG_INT(ClRaceSaveGhost, cl_race_save_ghost, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Save ghost")
|
||||
|
||||
MACRO_CONFIG_INT(ClDDRaceHud, cl_ddrace_hud, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable DDRace HUD")
|
||||
MACRO_CONFIG_INT(ClDDRaceScoreBoard, cl_ddrace_scoreboard, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable DDRace Scoreboard")
|
||||
MACRO_CONFIG_INT(ClShowFreezeBars, cl_show_freeze_bars, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show a freeze bar under frozen players to indicate the thaw time")
|
||||
MACRO_CONFIG_INT(SvResetPickups, sv_reset_pickups, 0, 0, 1, CFGFLAG_SERVER | CFGFLAG_GAME, "Whether the weapons are reset on passing the start tile or not")
|
||||
MACRO_CONFIG_INT(ClShowOthers, cl_show_others, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players in other teams (2 to show own team only)")
|
||||
MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players in other teams (alpha value, 0 invisible, 100 fully visible)")
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
fs_makedir(GetPath(TYPE_SAVE, "assets/entities", aPath, sizeof(aPath)));
|
||||
fs_makedir(GetPath(TYPE_SAVE, "assets/game", aPath, sizeof(aPath)));
|
||||
fs_makedir(GetPath(TYPE_SAVE, "assets/particles", aPath, sizeof(aPath)));
|
||||
fs_makedir(GetPath(TYPE_SAVE, "assets/hud", aPath, sizeof(aPath)));
|
||||
#if defined(CONF_VIDEORECORDER)
|
||||
fs_makedir(GetPath(TYPE_SAVE, "videos", aPath, sizeof(aPath)));
|
||||
#endif
|
||||
|
|
|
@ -28,25 +28,32 @@ void CDebugHud::RenderNetCorrections()
|
|||
/*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y),
|
||||
vec2(netobjects.local_character->x, netobjects.local_character->y));*/
|
||||
|
||||
float Velspeed = length(vec2(m_pClient->m_Snap.m_pLocalCharacter->m_VelX / 256.0f, m_pClient->m_Snap.m_pLocalCharacter->m_VelY / 256.0f)) * 50;
|
||||
const float TicksPerSecond = 50.0f;
|
||||
float Velspeed = length(vec2(m_pClient->m_Snap.m_pLocalCharacter->m_VelX / 256.0f, m_pClient->m_Snap.m_pLocalCharacter->m_VelY / 256.0f)) * TicksPerSecond;
|
||||
float VelspeedX = m_pClient->m_Snap.m_pLocalCharacter->m_VelX / 256.0f * TicksPerSecond;
|
||||
float VelspeedY = m_pClient->m_Snap.m_pLocalCharacter->m_VelY / 256.0f * TicksPerSecond;
|
||||
float Ramp = VelocityRamp(Velspeed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature);
|
||||
|
||||
const char *paStrings[] = {"velspeed:", "velspeed*ramp:", "ramp:", "checkpoint:", "Pos", " x:", " y:", "angle:", "netobj corrections", " num:", " on:"};
|
||||
const int Num = std::size(paStrings);
|
||||
const char *apStrings[] = {"velspeed:", "velspeed.x*ramp:", "velspeed.y:", "ramp:", "checkpoint:", "Pos", " x:", " y:", "angle:", "netobj corrections", " num:", " on:"};
|
||||
const int Num = std::size(apStrings);
|
||||
const float LineHeight = 6.0f;
|
||||
const float Fontsize = 5.0f;
|
||||
|
||||
float x = Width - 100.0f, y = 50.0f;
|
||||
for(int i = 0; i < Num; ++i)
|
||||
TextRender()->Text(0, x, y + i * LineHeight, Fontsize, paStrings[i], -1.0f);
|
||||
TextRender()->Text(0, x, y + i * LineHeight, Fontsize, apStrings[i], -1.0f);
|
||||
|
||||
x = Width - 10.0f;
|
||||
char aBuf[128];
|
||||
str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed / 32);
|
||||
str_format(aBuf, sizeof(aBuf), "%.2f Bps", Velspeed / 32);
|
||||
float w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
|
||||
TextRender()->Text(0, x - w, y, Fontsize, aBuf, -1.0f);
|
||||
y += LineHeight;
|
||||
str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed / 32 * Ramp);
|
||||
str_format(aBuf, sizeof(aBuf), "%.2f Bps", VelspeedX / 32 * Ramp);
|
||||
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
|
||||
TextRender()->Text(0, x - w, y, Fontsize, aBuf, -1.0f);
|
||||
y += LineHeight;
|
||||
str_format(aBuf, sizeof(aBuf), "%.2f Bps", VelspeedY / 32);
|
||||
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
|
||||
TextRender()->Text(0, x - w, y, Fontsize, aBuf, -1.0f);
|
||||
y += LineHeight;
|
||||
|
@ -125,25 +132,81 @@ void CDebugHud::RenderTuning()
|
|||
Count++;
|
||||
}
|
||||
|
||||
y = y + Count * 6;
|
||||
// Rander Velspeed.X*Ramp Graphs
|
||||
Graphics()->MapScreen(0, 0, Graphics()->ScreenWidth(), Graphics()->ScreenHeight());
|
||||
float GraphW = Graphics()->ScreenWidth() / 4.0f;
|
||||
float GraphH = Graphics()->ScreenHeight() / 6.0f;
|
||||
float sp = Graphics()->ScreenWidth() / 100.0f;
|
||||
float GraphX = GraphW;
|
||||
float GraphY = Graphics()->ScreenHeight() - GraphH - sp;
|
||||
|
||||
Graphics()->TextureClear();
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->LinesBegin();
|
||||
float Height = 50.0f;
|
||||
float pv = 1;
|
||||
IGraphics::CLineItem Array[100];
|
||||
for(int i = 0; i < 100; i++)
|
||||
CTuningParams *ClinetTuning = &m_pClient->m_Tuning[g_Config.m_ClDummy];
|
||||
const int StepSizeRampGraph = 270;
|
||||
const int StepSizeZoomedInGraph = 14;
|
||||
if(m_OldVelrampStart != ClinetTuning->m_VelrampStart || m_OldVelrampRange != ClinetTuning->m_VelrampRange || m_OldVelrampCurvature != ClinetTuning->m_VelrampCurvature)
|
||||
{
|
||||
float Speed = i / 100.0f * 3000;
|
||||
m_OldVelrampStart = ClinetTuning->m_VelrampStart;
|
||||
m_OldVelrampRange = ClinetTuning->m_VelrampRange;
|
||||
m_OldVelrampCurvature = ClinetTuning->m_VelrampCurvature;
|
||||
|
||||
m_RampGraph.Init(0.0f, 0.0f);
|
||||
m_SpeedTurningPoint = 0;
|
||||
float pv = 1;
|
||||
// CGraph must be fed with exactly 128 values.
|
||||
for(int i = 0; i < 128; i++)
|
||||
{
|
||||
// This is a calculation of the speed values per second on the X axis, from 270 to 34560 in steps of 270
|
||||
float Speed = (i + 1) * StepSizeRampGraph;
|
||||
float Ramp = VelocityRamp(Speed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature);
|
||||
float RampedSpeed = (Speed * Ramp) / 1000.0f;
|
||||
Array[i] = IGraphics::CLineItem((i - 1) * 2, y + Height - pv * Height, i * 2, y + Height - RampedSpeed * Height);
|
||||
//Graphics()->LinesDraw((i-1)*2, 200, i*2, 200);
|
||||
float RampedSpeed = Speed * Ramp;
|
||||
if(RampedSpeed >= pv)
|
||||
{
|
||||
m_RampGraph.InsertAt(i, RampedSpeed / 32, 0, 1, 0);
|
||||
m_SpeedTurningPoint = Speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RampGraph.InsertAt(i, RampedSpeed / 32, 1, 0, 0);
|
||||
}
|
||||
pv = RampedSpeed;
|
||||
}
|
||||
Graphics()->LinesDraw(Array, 100);
|
||||
Graphics()->LinesEnd();
|
||||
m_RampGraph.ScaleMin();
|
||||
m_RampGraph.ScaleMax();
|
||||
|
||||
m_ZoomedInGraph.Init(0.0f, 0.0f);
|
||||
pv = 1;
|
||||
MiddleOfZoomedInGraph = m_SpeedTurningPoint;
|
||||
for(int i = 0; i < 128; i++)
|
||||
{
|
||||
// This is a calculation of the speed values per second on the X axis, from (MiddleOfZoomedInGraph - 64 * StepSize) to (MiddleOfZoomedInGraph + 64 * StepSize)
|
||||
float Speed = MiddleOfZoomedInGraph - 64 * StepSizeZoomedInGraph + i * StepSizeZoomedInGraph;
|
||||
float Ramp = VelocityRamp(Speed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature);
|
||||
float RampedSpeed = Speed * Ramp;
|
||||
if(RampedSpeed >= pv)
|
||||
{
|
||||
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, 0, 1, 0);
|
||||
m_SpeedTurningPoint = Speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, 1, 0, 0);
|
||||
}
|
||||
if(i == 0)
|
||||
{
|
||||
m_ZoomedInGraph.m_Min = m_ZoomedInGraph.m_MinRange = RampedSpeed;
|
||||
}
|
||||
pv = RampedSpeed;
|
||||
}
|
||||
m_ZoomedInGraph.ScaleMin();
|
||||
m_ZoomedInGraph.ScaleMax();
|
||||
}
|
||||
char aBuf[128];
|
||||
str_format(aBuf, sizeof(aBuf), "Velspeed.X*Ramp in Bps (Velspeed %d to %d)", StepSizeRampGraph / 32, 128 * StepSizeRampGraph / 32);
|
||||
m_RampGraph.Render(Graphics(), Client()->GetDebugFont(), GraphX, GraphY - GraphH - sp, GraphW, GraphH, aBuf);
|
||||
str_format(aBuf, sizeof(aBuf), "Max Velspeed before it ramps off: %.2f Bps", m_SpeedTurningPoint / 32);
|
||||
TextRender()->Text(0x0, GraphX, GraphY - sp - GraphH - 12, 12, aBuf, -1.0f);
|
||||
str_format(aBuf, sizeof(aBuf), "Zoomed in on turning point (Velspeed %d to %d)", ((int)MiddleOfZoomedInGraph - 64 * StepSizeZoomedInGraph) / 32, ((int)MiddleOfZoomedInGraph + 64 * StepSizeZoomedInGraph) / 32);
|
||||
m_ZoomedInGraph.Render(Graphics(), Client()->GetDebugFont(), GraphX, GraphY, GraphW, GraphH, aBuf);
|
||||
TextRender()->TextColor(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#ifndef GAME_CLIENT_COMPONENTS_DEBUGHUD_H
|
||||
#define GAME_CLIENT_COMPONENTS_DEBUGHUD_H
|
||||
#include <engine/client/client.h>
|
||||
|
||||
#include <game/client/component.h>
|
||||
|
||||
class CDebugHud : public CComponent
|
||||
|
@ -10,6 +12,14 @@ class CDebugHud : public CComponent
|
|||
void RenderTuning();
|
||||
void RenderHint();
|
||||
|
||||
CGraph m_RampGraph;
|
||||
CGraph m_ZoomedInGraph;
|
||||
float m_SpeedTurningPoint;
|
||||
float MiddleOfZoomedInGraph;
|
||||
float m_OldVelrampStart;
|
||||
float m_OldVelrampRange;
|
||||
float m_OldVelrampCurvature;
|
||||
|
||||
public:
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
virtual void OnRender() override;
|
||||
|
|
239
src/game/client/components/freezebars.cpp
Normal file
239
src/game/client/components/freezebars.cpp
Normal file
|
@ -0,0 +1,239 @@
|
|||
#include <game/client/gameclient.h>
|
||||
|
||||
#include "freezebars.h"
|
||||
|
||||
void CFreezeBars::RenderFreezeBar(const int ClientID)
|
||||
{
|
||||
const float FreezeBarWidth = 64.0f;
|
||||
const float FreezeBarHalfWidth = 32.0f;
|
||||
const float FreezeBarHight = 16.0f;
|
||||
|
||||
// pCharacter contains the predicted character for local players or the last snap for players who are spectated
|
||||
CCharacterCore *pCharacter = &m_pClient->m_aClients[ClientID].m_Predicted;
|
||||
if(pCharacter->m_FreezeEnd <= 0.0f || !m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo || pCharacter->m_IsInFreeze)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int Max = pCharacter->m_FreezeEnd - pCharacter->m_FreezeTick;
|
||||
float FreezeProgress = clamp(Max - (Client()->GameTick(g_Config.m_ClDummy) - pCharacter->m_FreezeTick), 0, Max) / (float)Max;
|
||||
if(FreezeProgress <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 Position = m_pClient->m_aClients[ClientID].m_RenderPos;
|
||||
Position.x -= FreezeBarHalfWidth;
|
||||
Position.y += 32;
|
||||
|
||||
float Alpha = m_pClient->IsOtherTeam(ClientID) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
|
||||
|
||||
RenderFreezeBarPos(Position.x, Position.y, FreezeBarWidth, FreezeBarHight, FreezeProgress, Alpha);
|
||||
}
|
||||
|
||||
void CFreezeBars::RenderFreezeBarPos(float x, const float y, const float width, const float height, float Progress, const float Alpha)
|
||||
{
|
||||
Progress = clamp(Progress, 0.0f, 1.0f);
|
||||
|
||||
// what percentage of the end pieces is used for the progress indicator and how much is the rest
|
||||
// half of the ends are used for the progress display
|
||||
const float RestPct = 0.5f;
|
||||
const float ProgPct = 0.5f;
|
||||
|
||||
const float EndWidth = height; // to keep the correct scale - the height of the sprite is as long as the width
|
||||
const float BarHeight = height;
|
||||
const float WholeBarWidth = width;
|
||||
const float MiddleBarWidth = WholeBarWidth - (EndWidth * 2.0f);
|
||||
const float EndProgressWidth = EndWidth * ProgPct;
|
||||
const float EndRestWidth = EndWidth * RestPct;
|
||||
const float ProgressBarWidth = WholeBarWidth - (EndProgressWidth * 2.0f);
|
||||
const float EndProgressProportion = EndProgressWidth / ProgressBarWidth;
|
||||
const float MiddleProgressProportion = MiddleBarWidth / ProgressBarWidth;
|
||||
|
||||
// we cut 2% of all sides of all sprites so we don't get edge bleeding
|
||||
const float CutL = 0.02f;
|
||||
const float CutR = 0.98f;
|
||||
const float CutT = 0.02f;
|
||||
const float CutB = 0.98f;
|
||||
const float Slice = 0.02f;
|
||||
|
||||
// beginning piece
|
||||
float BeginningPieceProgress = 1;
|
||||
if(Progress <= EndProgressProportion)
|
||||
{
|
||||
BeginningPieceProgress = Progress / EndProgressProportion;
|
||||
}
|
||||
|
||||
// full
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// Subset: top_l, top_m, btm_m, btm_l
|
||||
Graphics()->QuadsSetSubsetFree(CutL, CutT, RestPct + (ProgPct - CutL) * BeginningPieceProgress, CutT, RestPct + (ProgPct - CutL) * BeginningPieceProgress, CutB, CutL, CutB);
|
||||
IGraphics::CQuadItem QuadFullBeginning(x, y, EndRestWidth + EndProgressWidth * BeginningPieceProgress, BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadFullBeginning, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
|
||||
// empty
|
||||
if(BeginningPieceProgress < 1.0f)
|
||||
{
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// Subset: top_m, top_l, btm_l, btm_m | it is mirrored on the horizontal axe and rotated 180 degrees
|
||||
Graphics()->QuadsSetSubsetFree(ProgPct - (ProgPct - CutL) * BeginningPieceProgress, CutT, CutL, CutT, CutL, CutB, ProgPct - (ProgPct - CutL) * BeginningPieceProgress, CutB);
|
||||
IGraphics::CQuadItem QuadEmptyBeginning(x + EndRestWidth + EndProgressWidth * BeginningPieceProgress, y, EndProgressWidth * (1.0f - BeginningPieceProgress), BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadEmptyBeginning, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
|
||||
// middle piece
|
||||
x += EndWidth;
|
||||
|
||||
float MiddlePieceProgress = 1;
|
||||
if(Progress <= EndProgressProportion + MiddleProgressProportion)
|
||||
{
|
||||
if(Progress <= EndProgressProportion)
|
||||
{
|
||||
MiddlePieceProgress = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MiddlePieceProgress = (Progress - EndProgressProportion) / MiddleProgressProportion;
|
||||
}
|
||||
}
|
||||
|
||||
const float FullMiddleBarWidth = MiddleBarWidth * MiddlePieceProgress;
|
||||
const float EmptyMiddleBarWidth = MiddleBarWidth - FullMiddleBarWidth;
|
||||
|
||||
// full freeze bar
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFull);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// select the middle portion of the sprite so we don't get edge bleeding
|
||||
if(FullMiddleBarWidth <= EndWidth)
|
||||
{
|
||||
// prevent pixel puree, select only a small slice
|
||||
// Subset: top_l, top_m, btm_m, btm_l
|
||||
Graphics()->QuadsSetSubsetFree(CutL, CutT, CutL + Slice, CutT, CutL + Slice, CutB, CutL, CutB);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subset: top_l, top_r, btm_r, btm_l
|
||||
Graphics()->QuadsSetSubsetFree(CutL, CutT, CutR, CutT, CutR, CutB, CutL, CutB);
|
||||
}
|
||||
IGraphics::CQuadItem QuadFull(x, y, FullMiddleBarWidth, BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadFull, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
|
||||
// empty freeze bar
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmpty);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// select the middle portion of the sprite so we don't get edge bleeding
|
||||
if(EmptyMiddleBarWidth <= EndWidth)
|
||||
{
|
||||
// prevent pixel puree, select only a small slice
|
||||
// Subset: top_m, top_l, btm_l, btm_m | it is mirrored on the horizontal axe and rotated 180 degrees
|
||||
Graphics()->QuadsSetSubsetFree(CutL + Slice, CutT, CutL, CutT, CutL, CutB, CutL + Slice, CutB);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subset: top_r, top_l, btm_l, btm_r | it is mirrored on the horizontal axe and rotated 180 degrees
|
||||
Graphics()->QuadsSetSubsetFree(CutR, CutT, CutL, CutT, CutL, CutB, CutR, CutB);
|
||||
}
|
||||
|
||||
IGraphics::CQuadItem QuadEmpty(x + FullMiddleBarWidth, y, EmptyMiddleBarWidth, BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadEmpty, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
|
||||
// end piece
|
||||
x += MiddleBarWidth;
|
||||
float EndingPieceProgress = 1;
|
||||
if(Progress <= 1)
|
||||
{
|
||||
if(Progress <= (EndProgressProportion + MiddleProgressProportion))
|
||||
{
|
||||
EndingPieceProgress = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
EndingPieceProgress = (Progress - EndProgressProportion - MiddleProgressProportion) / EndProgressProportion;
|
||||
}
|
||||
}
|
||||
if(EndingPieceProgress > 0.0f)
|
||||
{
|
||||
// full
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// Subset: top_r, top_m, btm_m, btm_r | it is mirrored on the horizontal axe and rotated 180 degrees
|
||||
Graphics()->QuadsSetSubsetFree(CutR, CutT, CutR - (ProgPct - CutL) * EndingPieceProgress, CutT, CutR - (ProgPct - CutL) * EndingPieceProgress, CutB, CutR, CutB);
|
||||
IGraphics::CQuadItem QuadFullEnding(x, y, EndProgressWidth * EndingPieceProgress, BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadFullEnding, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
// empty
|
||||
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
// Subset: top_m, top_r, btm_r, btm_m
|
||||
Graphics()->QuadsSetSubsetFree(ProgPct - (ProgPct - CutL) * (1.0f - EndingPieceProgress), CutT, CutR, CutT, CutR, CutB, ProgPct - (ProgPct - CutL) * (1.0f - EndingPieceProgress), CutB);
|
||||
IGraphics::CQuadItem QuadEmptyEnding(x + EndProgressWidth * EndingPieceProgress, y, EndProgressWidth * (1.0f - EndingPieceProgress) + EndRestWidth, BarHeight);
|
||||
Graphics()->QuadsDrawTL(&QuadEmptyEnding, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
inline bool CFreezeBars::IsPlayerInfoAvailable(int ClientID) const
|
||||
{
|
||||
const void *pPrevInfo = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_PLAYERINFO, ClientID);
|
||||
const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, ClientID);
|
||||
return pPrevInfo && pInfo;
|
||||
}
|
||||
|
||||
void CFreezeBars::OnRender()
|
||||
{
|
||||
if(!g_Config.m_ClShowFreezeBars)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// get screen edges to avoid rendering offscreen
|
||||
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
||||
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
||||
// expand the edges to prevent popping in/out onscreen
|
||||
//
|
||||
// it is assumed that the tee with the freeze bar fit into a 240x240 box centered on the tee
|
||||
// this may need to be changed or calculated differently in the future
|
||||
float BorderBuffer = 120;
|
||||
ScreenX0 -= BorderBuffer;
|
||||
ScreenX1 += BorderBuffer;
|
||||
ScreenY0 -= BorderBuffer;
|
||||
ScreenY1 += BorderBuffer;
|
||||
|
||||
int LocalClientID = m_pClient->m_Snap.m_LocalClientID;
|
||||
|
||||
// render everyone else's freeze bar, then our own
|
||||
for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++)
|
||||
{
|
||||
if(ClientID == LocalClientID || !m_pClient->m_Snap.m_aCharacters[ClientID].m_Active || !IsPlayerInfoAvailable(ClientID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//don't render if the tee is offscreen
|
||||
vec2 *pRenderPos = &m_pClient->m_aClients[ClientID].m_RenderPos;
|
||||
if(pRenderPos->x < ScreenX0 || pRenderPos->x > ScreenX1 || pRenderPos->y < ScreenY0 || pRenderPos->y > ScreenY1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderFreezeBar(ClientID);
|
||||
}
|
||||
if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID))
|
||||
{
|
||||
RenderFreezeBar(LocalClientID);
|
||||
}
|
||||
}
|
16
src/game/client/components/freezebars.h
Normal file
16
src/game/client/components/freezebars.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef GAME_CLIENT_COMPONENTS_FREEZEBARS_H
|
||||
#define GAME_CLIENT_COMPONENTS_FREEZEBARS_H
|
||||
#include <game/client/component.h>
|
||||
|
||||
class CFreezeBars : public CComponent
|
||||
{
|
||||
void RenderFreezeBar(const int ClientID);
|
||||
void RenderFreezeBarPos(float x, const float y, const float width, const float height, float Progress, float Alpha = 1.0f);
|
||||
bool IsPlayerInfoAvailable(int ClientID) const;
|
||||
|
||||
public:
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
virtual void OnRender() override;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -49,8 +49,13 @@ class CHud : public CComponent
|
|||
void RenderTeambalanceWarning();
|
||||
void RenderVoting();
|
||||
|
||||
void PrepareHealthAmoQuads();
|
||||
void RenderHealthAndAmmo(const CNetObj_Character *pCharacter);
|
||||
void PrepareAmmoHealthAndArmorQuads();
|
||||
void RenderAmmoHealthAndArmor(const CNetObj_Character *pCharacter);
|
||||
|
||||
void PreparePlayerStateQuads();
|
||||
void RenderPlayerState(const int ClientID);
|
||||
void RenderDummyActions();
|
||||
void RenderMovementInformation(const int ClientID);
|
||||
|
||||
void RenderGameTimer();
|
||||
void RenderPauseNotification();
|
||||
|
@ -62,6 +67,8 @@ class CHud : public CComponent
|
|||
|
||||
void MapscreenToGroup(float CenterX, float CenterY, struct CMapItemGroup *PGroup);
|
||||
|
||||
static constexpr float MOVEMENT_INFORMATION_LINE_HEIGHT = 8.0f;
|
||||
|
||||
public:
|
||||
CHud();
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
|
@ -75,6 +82,7 @@ public:
|
|||
// DDRace
|
||||
|
||||
virtual void OnMessage(int MsgType, void *pRawMsg) override;
|
||||
void RenderNinjaBarPos(float x, const float y, const float width, const float height, float Progress, float Alpha = 1.0f);
|
||||
|
||||
private:
|
||||
void RenderRecord();
|
||||
|
@ -87,6 +95,44 @@ private:
|
|||
int m_CheckpointTick;
|
||||
bool m_FinishTime;
|
||||
bool m_DDRaceTimeReceived;
|
||||
|
||||
inline float GetMovementInformationBoxHeight();
|
||||
inline int GetDigitsIndex(int Value, int Max);
|
||||
|
||||
// Quad Offsets
|
||||
int m_AmmoOffset[NUM_WEAPONS];
|
||||
int m_HealthOffset;
|
||||
int m_EmptyHealthOffset;
|
||||
int m_ArmorOffset;
|
||||
int m_EmptyArmorOffset;
|
||||
int m_CursorOffset[NUM_WEAPONS];
|
||||
int m_FlagOffset;
|
||||
int m_AirjumpOffset;
|
||||
int m_AirjumpEmptyOffset;
|
||||
int m_WeaponHammerOffset;
|
||||
int m_WeaponGunOffset;
|
||||
int m_WeaponShotgunOffset;
|
||||
int m_WeaponGrenadeOffset;
|
||||
int m_WeaponLaserOffset;
|
||||
int m_WeaponNinjaOffset;
|
||||
int m_EndlessJumpOffset;
|
||||
int m_EndlessHookOffset;
|
||||
int m_JetpackOffset;
|
||||
int m_TeleportGrenadeOffset;
|
||||
int m_TeleportGunOffset;
|
||||
int m_TeleportLaserOffset;
|
||||
int m_SoloOffset;
|
||||
int m_NoCollisionOffset;
|
||||
int m_NoHookHitOffset;
|
||||
int m_NoHammerHitOffset;
|
||||
int m_NoShotgunHitOffset;
|
||||
int m_NoGrenadeHitOffset;
|
||||
int m_NoLaserHitOffset;
|
||||
int m_DeepFrozenOffset;
|
||||
int m_LiveFrozenOffset;
|
||||
int m_DummyHammerOffset;
|
||||
int m_DummyCopyOffset;
|
||||
int m_PracticeModeOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -128,74 +128,46 @@ void CItems::RenderProjectile(const CProjectileData *pCurrent, int ItemID)
|
|||
{
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponProjectiles[CurWeapon]);
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
|
||||
|
||||
int QuadOffset = 2 + 8 + NUM_WEAPONS + CurWeapon;
|
||||
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, QuadOffset, Pos.x, Pos.y);
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, m_ProjectileOffset[CurWeapon], Pos.x, Pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted)
|
||||
{
|
||||
const int c[] = {
|
||||
SPRITE_PICKUP_HEALTH,
|
||||
SPRITE_PICKUP_ARMOR,
|
||||
SPRITE_PICKUP_WEAPON,
|
||||
SPRITE_PICKUP_NINJA,
|
||||
SPRITE_PICKUP_ARMOR_SHOTGUN,
|
||||
SPRITE_PICKUP_ARMOR_GRENADE,
|
||||
SPRITE_PICKUP_ARMOR_NINJA,
|
||||
SPRITE_PICKUP_ARMOR_LASER};
|
||||
|
||||
int CurWeapon = clamp(pCurrent->m_Subtype, 0, NUM_WEAPONS - 1);
|
||||
|
||||
if(c[pCurrent->m_Type] == SPRITE_PICKUP_HEALTH)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupHealth);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_ARMOR)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmor);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_ARMOR_SHOTGUN)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmorShotgun);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_ARMOR_GRENADE)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmorGrenade);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_ARMOR_LASER)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmorLaser);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_ARMOR_NINJA)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmorNinja);
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_WEAPON)
|
||||
{
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupWeapons[CurWeapon]);
|
||||
}
|
||||
else if(c[pCurrent->m_Type] == SPRITE_PICKUP_NINJA)
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupNinja);
|
||||
Graphics()->QuadsSetRotation(0);
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
int QuadOffset = 2;
|
||||
|
||||
float Angle = 0.0f;
|
||||
float IntraTick = IsPredicted ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy);
|
||||
vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), IntraTick);
|
||||
float Angle = 0.0f;
|
||||
if(pCurrent->m_Type == POWERUP_WEAPON)
|
||||
if(pCurrent->m_Type == POWERUP_HEALTH)
|
||||
{
|
||||
Angle = 0; //-pi/6;//-0.25f * pi * 2.0f;
|
||||
QuadOffset += 2 + CurWeapon;
|
||||
QuadOffset = m_PickupHealthOffset;
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupHealth);
|
||||
}
|
||||
else
|
||||
else if(pCurrent->m_Type == POWERUP_ARMOR)
|
||||
{
|
||||
QuadOffset += pCurrent->m_Type;
|
||||
|
||||
if(c[pCurrent->m_Type] == SPRITE_PICKUP_NINJA)
|
||||
QuadOffset = m_PickupArmorOffset;
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupArmor);
|
||||
}
|
||||
else if(pCurrent->m_Type == POWERUP_WEAPON)
|
||||
{
|
||||
QuadOffset = 2 + 8 - 1; // ninja is the last weapon
|
||||
QuadOffset = m_PickupWeaponOffset[CurWeapon];
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupWeapons[CurWeapon]);
|
||||
}
|
||||
else if(pCurrent->m_Type == POWERUP_NINJA)
|
||||
{
|
||||
QuadOffset = m_PickupNinjaOffset;
|
||||
m_pClient->m_Effects.PowerupShine(Pos, vec2(96, 18));
|
||||
Pos.x -= 10.0f;
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupNinja);
|
||||
}
|
||||
else if(c[pCurrent->m_Type] >= SPRITE_PICKUP_ARMOR_SHOTGUN && c[pCurrent->m_Type] <= SPRITE_PICKUP_ARMOR_NINJA)
|
||||
else if(pCurrent->m_Type >= POWERUP_ARMOR_SHOTGUN && pCurrent->m_Type <= POWERUP_ARMOR_LASER)
|
||||
{
|
||||
QuadOffset = m_WeaponArmorQuadOffset + (c[pCurrent->m_Type] - SPRITE_PICKUP_ARMOR_SHOTGUN);
|
||||
QuadOffset = m_PickupWeaponArmorOffset[pCurrent->m_Type - POWERUP_ARMOR_SHOTGUN];
|
||||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpritePickupWeaponArmor[pCurrent->m_Type - POWERUP_ARMOR_SHOTGUN]);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics()->QuadsSetRotation(0);
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
||||
Graphics()->QuadsSetRotation(Angle);
|
||||
|
||||
static float s_Time = 0.0f;
|
||||
|
@ -230,10 +202,15 @@ void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent,
|
|||
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagBlue);
|
||||
Graphics()->QuadsSetRotation(0);
|
||||
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
||||
int QuadOffset = 0;
|
||||
|
||||
if(pCurrent->m_Team != TEAM_RED)
|
||||
++QuadOffset;
|
||||
int QuadOffset;
|
||||
if(pCurrent->m_Team == TEAM_RED)
|
||||
{
|
||||
QuadOffset = m_RedFlagOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
QuadOffset = m_BlueFlagOffset;
|
||||
}
|
||||
|
||||
Graphics()->QuadsSetRotation(Angle);
|
||||
|
||||
|
@ -317,14 +294,12 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted)
|
|||
// render head
|
||||
{
|
||||
int CurParticle = (Client()->GameTick(g_Config.m_ClDummy) % 3);
|
||||
int QuadOffset = 2 + 8 + NUM_WEAPONS * 2 + CurParticle;
|
||||
|
||||
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_SpriteParticleSplat[CurParticle]);
|
||||
Graphics()->QuadsSetRotation(Client()->GameTick(g_Config.m_ClDummy));
|
||||
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, QuadOffset, Pos.x, Pos.y);
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, m_ParticleSplatOffset[CurParticle], Pos.x, Pos.y);
|
||||
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, QuadOffset, Pos.x, Pos.y, 20.f / 24.f, 20.f / 24.f);
|
||||
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, m_ParticleSplatOffset[CurParticle], Pos.x, Pos.y, 20.f / 24.f, 20.f / 24.f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,68 +490,46 @@ void CItems::OnInit()
|
|||
m_ItemsQuadContainerIndex = Graphics()->CreateQuadContainer(false);
|
||||
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, -21.f, -42.f, 42.f, 84.f);
|
||||
m_RedFlagOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, -21.f, -42.f, 42.f, 84.f);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, -21.f, -42.f, 42.f, 84.f);
|
||||
m_BlueFlagOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, -21.f, -42.f, 42.f, 84.f);
|
||||
|
||||
float ScaleX, ScaleY;
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_HEALTH, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
m_PickupHealthOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
RenderTools()->GetSpriteScale(&client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_HAMMER], ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, client_data7::g_pData->m_Weapons.m_aId[WEAPON_HAMMER].m_VisualSize * ScaleX, client_data7::g_pData->m_Weapons.m_aId[WEAPON_HAMMER].m_VisualSize * ScaleY);
|
||||
RenderTools()->GetSpriteScale(&client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_GUN], ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, client_data7::g_pData->m_Weapons.m_aId[WEAPON_GUN].m_VisualSize * ScaleX, client_data7::g_pData->m_Weapons.m_aId[WEAPON_GUN].m_VisualSize * ScaleY);
|
||||
RenderTools()->GetSpriteScale(&client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_SHOTGUN], ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, client_data7::g_pData->m_Weapons.m_aId[WEAPON_SHOTGUN].m_VisualSize * ScaleX, client_data7::g_pData->m_Weapons.m_aId[WEAPON_SHOTGUN].m_VisualSize * ScaleY);
|
||||
RenderTools()->GetSpriteScale(&client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_GRENADE], ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, client_data7::g_pData->m_Weapons.m_aId[WEAPON_GRENADE].m_VisualSize * ScaleX, client_data7::g_pData->m_Weapons.m_aId[WEAPON_GRENADE].m_VisualSize * ScaleY);
|
||||
RenderTools()->GetSpriteScale(&client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_LASER], ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, client_data7::g_pData->m_Weapons.m_aId[WEAPON_LASER].m_VisualSize * ScaleX, client_data7::g_pData->m_Weapons.m_aId[WEAPON_LASER].m_VisualSize * ScaleY);
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_NINJA, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 128.f * ScaleX, 128.f * ScaleY);
|
||||
m_PickupArmorOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
|
||||
for(int i = 0; i < NUM_WEAPONS; ++i)
|
||||
{
|
||||
RenderTools()->GetSpriteScale(g_pData->m_Weapons.m_aId[i].m_pSpriteBody, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleX, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleY);
|
||||
m_PickupWeaponOffset[i] = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleX, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleY);
|
||||
}
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_NINJA, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
m_PickupNinjaOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 128.f * ScaleX, 128.f * ScaleY);
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR_SHOTGUN + i, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
m_PickupWeaponArmorOffset[i] = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
}
|
||||
|
||||
for(int i = 0; i < NUM_WEAPONS; ++i)
|
||||
for(int &ProjectileOffset : m_ProjectileOffset)
|
||||
{
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 32.f);
|
||||
ProjectileOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 32.f);
|
||||
}
|
||||
|
||||
for(int &ParticleSplatOffset : m_ParticleSplatOffset)
|
||||
{
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 24.f);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 24.f);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 24.f);
|
||||
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR_SHOTGUN, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
m_WeaponArmorQuadOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR_GRENADE, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR_NINJA, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
RenderTools()->GetSpriteScale(SPRITE_PICKUP_ARMOR_LASER, ScaleX, ScaleY);
|
||||
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 64.f * ScaleX, 64.f * ScaleY);
|
||||
ParticleSplatOffset = RenderTools()->QuadContainerAddSprite(m_ItemsQuadContainerIndex, 24.f);
|
||||
}
|
||||
|
||||
Graphics()->QuadContainerUpload(m_ItemsQuadContainerIndex);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#ifndef GAME_CLIENT_COMPONENTS_ITEMS_H
|
||||
#define GAME_CLIENT_COMPONENTS_ITEMS_H
|
||||
#include <game/client/component.h>
|
||||
#include <game/generated/protocol.h>
|
||||
|
||||
class CProjectileData;
|
||||
|
||||
|
@ -15,14 +16,23 @@ class CItems : public CComponent
|
|||
|
||||
int m_ItemsQuadContainerIndex;
|
||||
|
||||
int m_WeaponArmorQuadOffset = 0;
|
||||
|
||||
public:
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
virtual void OnRender() override;
|
||||
virtual void OnInit() override;
|
||||
|
||||
void ReconstructSmokeTrail(const CProjectileData *pCurrent, int DestroyTick);
|
||||
|
||||
private:
|
||||
int m_BlueFlagOffset;
|
||||
int m_RedFlagOffset;
|
||||
int m_PickupHealthOffset;
|
||||
int m_PickupArmorOffset;
|
||||
int m_PickupWeaponOffset[NUM_WEAPONS];
|
||||
int m_PickupNinjaOffset;
|
||||
int m_PickupWeaponArmorOffset[4];
|
||||
int m_ProjectileOffset[NUM_WEAPONS];
|
||||
int m_ParticleSplatOffset[3];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1038,6 +1038,7 @@ void CMenus::OnInit()
|
|||
Console()->Chain("cl_asset_game", ConchainAssetGame, this);
|
||||
Console()->Chain("cl_asset_emoticons", ConchainAssetEmoticons, this);
|
||||
Console()->Chain("cl_asset_particles", ConchainAssetParticles, this);
|
||||
Console()->Chain("cl_asset_hud", ConchainAssetHud, this);
|
||||
|
||||
m_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
|
||||
|
||||
|
|
|
@ -246,11 +246,16 @@ public:
|
|||
{
|
||||
};
|
||||
|
||||
struct SCustomHud : public SCustomItem
|
||||
{
|
||||
};
|
||||
|
||||
protected:
|
||||
sorted_array<SCustomEntities> m_EntitiesList;
|
||||
sorted_array<SCustomGame> m_GameList;
|
||||
sorted_array<SCustomEmoticon> m_EmoticonList;
|
||||
sorted_array<SCustomParticle> m_ParticlesList;
|
||||
sorted_array<SCustomHud> m_HudList;
|
||||
|
||||
bool m_IsInit = false;
|
||||
|
||||
|
@ -260,11 +265,13 @@ protected:
|
|||
static int GameScan(const char *pName, int IsDir, int DirType, void *pUser);
|
||||
static int EmoticonsScan(const char *pName, int IsDir, int DirType, void *pUser);
|
||||
static int ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser);
|
||||
static int HudScan(const char *pName, int IsDir, int DirType, void *pUser);
|
||||
|
||||
static void ConchainAssetsEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainAssetGame(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainAssetParticles(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainAssetEmoticons(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainAssetHud(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
|
||||
void ClearCustomItems(int CurTab);
|
||||
|
||||
|
|
|
@ -2354,8 +2354,14 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
|
|||
// ***** HUD ***** //
|
||||
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhud, Localize("Show ingame HUD"), &g_Config.m_ClShowhud, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClDDRaceHud, Localize("Use DDRace HUD"), &g_Config.m_ClDDRaceHud, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClDDRaceScoreBoard, Localize("Use DDRace Scoreboard"), &g_Config.m_ClDDRaceScoreBoard, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudDummyActions, Localize("Show dummy actions"), &g_Config.m_ClShowhudDummyActions, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowFreezeBars, Localize("Show freeze bars"), &g_Config.m_ClShowFreezeBars, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowIDs, Localize("Show client IDs"), &g_Config.m_ClShowIDs, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudPlayerPosition, Localize("Show player position"), &g_Config.m_ClShowhudPlayerPosition, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudPlayerSpeed, Localize("Show player speed"), &g_Config.m_ClShowhudPlayerSpeed, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudPlayerAngle, Localize("Show player target angle"), &g_Config.m_ClShowhudPlayerAngle, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudScore, Localize("Show score"), &g_Config.m_ClShowhudScore, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudHealthAmmo, Localize("Show health + ammo"), &g_Config.m_ClShowhudHealthAmmo, &MainView, LineMargin);
|
||||
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowChat, Localize("Show chat"), &g_Config.m_ClShowChat, &MainView, LineMargin);
|
||||
|
|
|
@ -15,6 +15,16 @@ struct SMenuAssetScanUser
|
|||
TMenuAssetScanLoadedFunc m_LoadedFunc;
|
||||
};
|
||||
|
||||
// IDs of the tabs in the Assets menu
|
||||
enum
|
||||
{
|
||||
ASSETS_TAB_ENTITIES = 0,
|
||||
ASSETS_TAB_GAME = 1,
|
||||
ASSETS_TAB_EMOTICONS = 2,
|
||||
ASSETS_TAB_PARTICLES = 3,
|
||||
ASSETS_TAB_HUD = 4
|
||||
};
|
||||
|
||||
void CMenus::LoadEntities(SCustomEntities *pEntitiesItem, void *pUser)
|
||||
{
|
||||
auto *pRealUser = (SMenuAssetScanUser *)pUser;
|
||||
|
@ -208,33 +218,44 @@ int CMenus::ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser
|
|||
return AssetScan(pName, IsDir, DirType, pThis->m_ParticlesList, "particles", pGraphics, pUser);
|
||||
}
|
||||
|
||||
int CMenus::HudScan(const char *pName, int IsDir, int DirType, void *pUser)
|
||||
{
|
||||
CMenus *pMenus = (CMenus *)pUser;
|
||||
IGraphics *pGraphics = pMenus->Graphics();
|
||||
return AssetScan(pName, IsDir, DirType, pMenus->m_HudList, "hud", pGraphics, pUser);
|
||||
}
|
||||
|
||||
static sorted_array<const CMenus::SCustomEntities *> s_SearchEntitiesList;
|
||||
static sorted_array<const CMenus::SCustomGame *> s_SearchGamesList;
|
||||
static sorted_array<const CMenus::SCustomEmoticon *> s_SearchEmoticonsList;
|
||||
static sorted_array<const CMenus::SCustomParticle *> s_SearchParticlesList;
|
||||
static sorted_array<const CMenus::SCustomHud *> s_SearchHudList;
|
||||
|
||||
static bool s_InitCustomList[4] = {
|
||||
static const int NumberOfAssetsTabs = 5;
|
||||
static bool s_InitCustomList[NumberOfAssetsTabs] = {
|
||||
true,
|
||||
};
|
||||
|
||||
static int s_CustomListSize[4] = {
|
||||
static int s_CustomListSize[NumberOfAssetsTabs] = {
|
||||
0,
|
||||
};
|
||||
|
||||
static char s_aFilterString[4][50];
|
||||
static char s_aFilterString[NumberOfAssetsTabs][50];
|
||||
|
||||
static int s_CurCustomTab = 0;
|
||||
static int s_CurCustomTab = ASSETS_TAB_ENTITIES;
|
||||
|
||||
static const CMenus::SCustomItem *GetCustomItem(int CurTab, int Index)
|
||||
{
|
||||
if(CurTab == 0)
|
||||
if(CurTab == ASSETS_TAB_ENTITIES)
|
||||
return s_SearchEntitiesList[Index];
|
||||
else if(CurTab == 1)
|
||||
else if(CurTab == ASSETS_TAB_GAME)
|
||||
return s_SearchGamesList[Index];
|
||||
else if(CurTab == 2)
|
||||
else if(CurTab == ASSETS_TAB_EMOTICONS)
|
||||
return s_SearchEmoticonsList[Index];
|
||||
else if(CurTab == 3)
|
||||
else if(CurTab == ASSETS_TAB_PARTICLES)
|
||||
return s_SearchParticlesList[Index];
|
||||
else if(CurTab == ASSETS_TAB_HUD)
|
||||
return s_SearchHudList[Index];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -253,7 +274,7 @@ void ClearAssetList(sorted_array<TName> &List, IGraphics *pGraphics)
|
|||
|
||||
void CMenus::ClearCustomItems(int CurTab)
|
||||
{
|
||||
if(CurTab == 0)
|
||||
if(CurTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
for(int i = 0; i < m_EntitiesList.size(); ++i)
|
||||
{
|
||||
|
@ -269,27 +290,34 @@ void CMenus::ClearCustomItems(int CurTab)
|
|||
// reload current entities
|
||||
m_pClient->m_MapImages.ChangeEntitiesPath(g_Config.m_ClAssetsEntites);
|
||||
}
|
||||
else if(CurTab == 1)
|
||||
else if(CurTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
ClearAssetList(m_GameList, Graphics());
|
||||
|
||||
// reload current game skin
|
||||
GameClient()->LoadGameSkin(g_Config.m_ClAssetGame);
|
||||
}
|
||||
else if(CurTab == 2)
|
||||
else if(CurTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
ClearAssetList(m_EmoticonList, Graphics());
|
||||
|
||||
// reload current emoticons skin
|
||||
GameClient()->LoadEmoticonsSkin(g_Config.m_ClAssetEmoticons);
|
||||
}
|
||||
else if(CurTab == 3)
|
||||
else if(CurTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
ClearAssetList(m_ParticlesList, Graphics());
|
||||
|
||||
// reload current particles skin
|
||||
GameClient()->LoadParticlesSkin(g_Config.m_ClAssetParticles);
|
||||
}
|
||||
else if(CurTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
ClearAssetList(m_HudList, Graphics());
|
||||
|
||||
// reload current hud skin
|
||||
GameClient()->LoadHudSkin(g_Config.m_ClAssetHud);
|
||||
}
|
||||
s_InitCustomList[CurTab] = true;
|
||||
}
|
||||
|
||||
|
@ -330,24 +358,27 @@ int InitSearchList(sorted_array<const TName *> &SearchList, sorted_array<TName>
|
|||
|
||||
void CMenus::RenderSettingsCustom(CUIRect MainView)
|
||||
{
|
||||
CUIRect Label, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, Page1Tab, Page2Tab, Page3Tab, Page4Tab, ReloadButton;
|
||||
CUIRect Label, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, Page1Tab, Page2Tab, Page3Tab, Page4Tab, Page5Tab, ReloadButton;
|
||||
|
||||
MainView.HSplitTop(20, &Label, &MainView);
|
||||
float TabsW = Label.w;
|
||||
Label.VSplitLeft(TabsW / 4, &Page1Tab, &Page2Tab);
|
||||
Page2Tab.VSplitLeft(TabsW / 4, &Page2Tab, &Page3Tab);
|
||||
Page3Tab.VSplitLeft(TabsW / 4, &Page3Tab, &Page4Tab);
|
||||
Label.VSplitLeft(TabsW / NumberOfAssetsTabs, &Page1Tab, &Page2Tab);
|
||||
Page2Tab.VSplitLeft(TabsW / NumberOfAssetsTabs, &Page2Tab, &Page3Tab);
|
||||
Page3Tab.VSplitLeft(TabsW / NumberOfAssetsTabs, &Page3Tab, &Page4Tab);
|
||||
Page4Tab.VSplitLeft(TabsW / NumberOfAssetsTabs, &Page4Tab, &Page5Tab);
|
||||
|
||||
static int s_aPageTabs[4] = {};
|
||||
static int s_aPageTabs[NumberOfAssetsTabs] = {};
|
||||
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[0], Localize("Entities"), s_CurCustomTab == 0, &Page1Tab, 5, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = 0;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[1], Localize("Game"), s_CurCustomTab == 1, &Page2Tab, 0, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = 1;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[2], Localize("Emoticons"), s_CurCustomTab == 2, &Page3Tab, 0, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = 2;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[3], Localize("Particles"), s_CurCustomTab == 3, &Page4Tab, 10, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = 3;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[0], Localize("Entities"), s_CurCustomTab == ASSETS_TAB_ENTITIES, &Page1Tab, 5, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = ASSETS_TAB_ENTITIES;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[1], Localize("Game"), s_CurCustomTab == ASSETS_TAB_GAME, &Page2Tab, 0, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = ASSETS_TAB_GAME;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[2], Localize("Emoticons"), s_CurCustomTab == ASSETS_TAB_EMOTICONS, &Page3Tab, 0, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = ASSETS_TAB_EMOTICONS;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[3], Localize("Particles"), s_CurCustomTab == ASSETS_TAB_PARTICLES, &Page4Tab, 0, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = ASSETS_TAB_PARTICLES;
|
||||
if(DoButton_MenuTab((void *)&s_aPageTabs[4], Localize("HUD"), s_CurCustomTab == ASSETS_TAB_HUD, &Page5Tab, 10, NULL, NULL, NULL, NULL, 4))
|
||||
s_CurCustomTab = ASSETS_TAB_HUD;
|
||||
|
||||
int64_t LoadStartTime = time_get_microseconds();
|
||||
SMenuAssetScanUser User;
|
||||
|
@ -356,7 +387,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
if(time_get_microseconds() - LoadStartTime > 500000)
|
||||
RenderLoading(false, false);
|
||||
};
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
if(m_EntitiesList.size() == 0)
|
||||
{
|
||||
|
@ -371,18 +402,22 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
if(m_EntitiesList.size() != s_CustomListSize[s_CurCustomTab])
|
||||
s_InitCustomList[s_CurCustomTab] = true;
|
||||
}
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
InitAssetList(m_GameList, "assets/game", "game", GameScan, Graphics(), Storage(), &User);
|
||||
}
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
InitAssetList(m_EmoticonList, "assets/emoticons", "emoticons", EmoticonsScan, Graphics(), Storage(), &User);
|
||||
}
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
InitAssetList(m_ParticlesList, "assets/particles", "particles", ParticlesScan, Graphics(), Storage(), &User);
|
||||
}
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
InitAssetList(m_HudList, "assets/hud", "hud", HudScan, Graphics(), Storage(), this);
|
||||
}
|
||||
|
||||
MainView.HSplitTop(10.0f, 0, &MainView);
|
||||
|
||||
|
@ -392,7 +427,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
if(s_InitCustomList[s_CurCustomTab])
|
||||
{
|
||||
int ListSize = 0;
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
s_SearchEntitiesList.clear();
|
||||
ListSize = m_EntitiesList.size();
|
||||
|
@ -407,18 +442,22 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
s_SearchEntitiesList.add_unsorted(s);
|
||||
}
|
||||
}
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
ListSize = InitSearchList(s_SearchGamesList, m_GameList);
|
||||
}
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
ListSize = InitSearchList(s_SearchEmoticonsList, m_EmoticonList);
|
||||
}
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
ListSize = InitSearchList(s_SearchParticlesList, m_ParticlesList);
|
||||
}
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
ListSize = InitSearchList(s_SearchHudList, m_HudList);
|
||||
}
|
||||
s_InitCustomList[s_CurCustomTab] = false;
|
||||
s_CustomListSize[s_CurCustomTab] = ListSize;
|
||||
}
|
||||
|
@ -430,23 +469,28 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
|
||||
int SearchListSize = 0;
|
||||
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
SearchListSize = s_SearchEntitiesList.size();
|
||||
}
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
SearchListSize = s_SearchGamesList.size();
|
||||
TextureHeight = 75;
|
||||
}
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
SearchListSize = s_SearchEmoticonsList.size();
|
||||
}
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
SearchListSize = s_SearchParticlesList.size();
|
||||
}
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
SearchListSize = s_SearchHudList.size();
|
||||
TextureHeight = 128;
|
||||
}
|
||||
|
||||
UiDoListboxStart(&s_InitCustomList[s_CurCustomTab], &CustomList, TextureHeight + 15.0f + 10.0f + Margin, "", "", SearchListSize, CustomList.w / (Margin + TextureWidth), OldSelected, s_ScrollValue, true);
|
||||
for(int i = 0; i < SearchListSize; ++i)
|
||||
|
@ -455,26 +499,31 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
if(s == NULL)
|
||||
continue;
|
||||
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
if(str_comp(s->m_aName, g_Config.m_ClAssetsEntites) == 0)
|
||||
OldSelected = i;
|
||||
}
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
if(str_comp(s->m_aName, g_Config.m_ClAssetGame) == 0)
|
||||
OldSelected = i;
|
||||
}
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
if(str_comp(s->m_aName, g_Config.m_ClAssetEmoticons) == 0)
|
||||
OldSelected = i;
|
||||
}
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
if(str_comp(s->m_aName, g_Config.m_ClAssetParticles) == 0)
|
||||
OldSelected = i;
|
||||
}
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
if(str_comp(s->m_aName, g_Config.m_ClAssetHud) == 0)
|
||||
OldSelected = i;
|
||||
}
|
||||
|
||||
CListboxItem Item = UiDoListboxNextItem(s, OldSelected == i);
|
||||
CUIRect ItemRect = Item.m_Rect;
|
||||
|
@ -504,26 +553,31 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
{
|
||||
if(GetCustomItem(s_CurCustomTab, NewSelected)->m_aName[0] != '\0')
|
||||
{
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
{
|
||||
str_copy(g_Config.m_ClAssetsEntites, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName, sizeof(g_Config.m_ClAssetsEntites));
|
||||
m_pClient->m_MapImages.ChangeEntitiesPath(GetCustomItem(s_CurCustomTab, NewSelected)->m_aName);
|
||||
}
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
{
|
||||
str_copy(g_Config.m_ClAssetGame, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName, sizeof(g_Config.m_ClAssetGame));
|
||||
GameClient()->LoadGameSkin(g_Config.m_ClAssetGame);
|
||||
}
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
{
|
||||
str_copy(g_Config.m_ClAssetEmoticons, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName, sizeof(g_Config.m_ClAssetEmoticons));
|
||||
GameClient()->LoadEmoticonsSkin(g_Config.m_ClAssetEmoticons);
|
||||
}
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
{
|
||||
str_copy(g_Config.m_ClAssetParticles, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName, sizeof(g_Config.m_ClAssetParticles));
|
||||
GameClient()->LoadParticlesSkin(g_Config.m_ClAssetParticles);
|
||||
}
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
{
|
||||
str_copy(g_Config.m_ClAssetHud, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName, sizeof(g_Config.m_ClAssetHud));
|
||||
GameClient()->LoadHudSkin(g_Config.m_ClAssetHud);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,14 +621,16 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
{
|
||||
char aBuf[IO_MAX_PATH_LENGTH];
|
||||
char aBufFull[IO_MAX_PATH_LENGTH + 7];
|
||||
if(s_CurCustomTab == 0)
|
||||
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
|
||||
str_copy(aBufFull, "assets/entities", sizeof(aBufFull));
|
||||
else if(s_CurCustomTab == 1)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_GAME)
|
||||
str_copy(aBufFull, "assets/game", sizeof(aBufFull));
|
||||
else if(s_CurCustomTab == 2)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS)
|
||||
str_copy(aBufFull, "assets/emoticons", sizeof(aBufFull));
|
||||
else if(s_CurCustomTab == 3)
|
||||
else if(s_CurCustomTab == ASSETS_TAB_PARTICLES)
|
||||
str_copy(aBufFull, "assets/particles", sizeof(aBufFull));
|
||||
else if(s_CurCustomTab == ASSETS_TAB_HUD)
|
||||
str_copy(aBufFull, "assets/hud", sizeof(aBufFull));
|
||||
Storage()->GetCompletePath(IStorage::TYPE_SAVE, aBufFull, aBuf, sizeof(aBuf));
|
||||
Storage()->CreateFolder("assets", IStorage::TYPE_SAVE);
|
||||
Storage()->CreateFolder(aBufFull, IStorage::TYPE_SAVE);
|
||||
|
@ -654,3 +710,18 @@ void CMenus::ConchainAssetEmoticons(IConsole::IResult *pResult, void *pUserData,
|
|||
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
}
|
||||
|
||||
void CMenus::ConchainAssetHud(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
CMenus *pThis = (CMenus *)pUserData;
|
||||
if(pResult->NumArguments() == 1)
|
||||
{
|
||||
const char *pArg = pResult->GetString(0);
|
||||
if(str_comp(pArg, g_Config.m_ClAssetHud) != 0)
|
||||
{
|
||||
pThis->GameClient()->LoadHudSkin(pArg);
|
||||
}
|
||||
}
|
||||
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
}
|
||||
|
|
|
@ -378,10 +378,8 @@ void CNamePlates::OnInit()
|
|||
{
|
||||
ResetNamePlates();
|
||||
|
||||
// the direction
|
||||
// Quad for the direction arrows above the player
|
||||
m_DirectionQuadContainerIndex = Graphics()->CreateQuadContainer(false);
|
||||
|
||||
IGraphics::CQuadItem QuadItem(0.f, 0.f, 22.f, 22.f);
|
||||
Graphics()->QuadContainerAddQuads(m_DirectionQuadContainerIndex, &QuadItem, 1);
|
||||
RenderTools()->QuadContainerAddSprite(m_DirectionQuadContainerIndex, 0.f, 0.f, 22.f);
|
||||
Graphics()->QuadContainerUpload(m_DirectionQuadContainerIndex);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,54 @@ inline float AngularApproach(float Src, float Dst, float Amount)
|
|||
return Dst;
|
||||
return n;
|
||||
}
|
||||
|
||||
float CPlayers::GetPlayerTargetAngle(
|
||||
const CNetObj_Character *pPrevChar,
|
||||
const CNetObj_Character *pPlayerChar,
|
||||
int ClientID,
|
||||
float Intra)
|
||||
{
|
||||
float AngleIntraTick = Intra;
|
||||
// using unpredicted angle when rendering other players in-game
|
||||
if(ClientID >= 0)
|
||||
AngleIntraTick = Client()->IntraGameTick(g_Config.m_ClDummy);
|
||||
if(ClientID >= 0 && m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo)
|
||||
{
|
||||
CNetObj_DDNetCharacterDisplayInfo *CharacterDisplayInfo = &m_pClient->m_Snap.m_aCharacters[ClientID].m_ExtendedDisplayInfo;
|
||||
if(m_pClient->m_Snap.m_aCharacters[ClientID].m_PrevExtendedDisplayInfo)
|
||||
{
|
||||
const CNetObj_DDNetCharacterDisplayInfo *PrevCharacterDisplayInfo = m_pClient->m_Snap.m_aCharacters[ClientID].m_PrevExtendedDisplayInfo;
|
||||
|
||||
float MixX = mix((float)PrevCharacterDisplayInfo->m_TargetX, (float)CharacterDisplayInfo->m_TargetX, AngleIntraTick);
|
||||
float MixY = mix((float)PrevCharacterDisplayInfo->m_TargetY, (float)CharacterDisplayInfo->m_TargetY, AngleIntraTick);
|
||||
|
||||
return angle(vec2(MixX, MixY));
|
||||
}
|
||||
else
|
||||
{
|
||||
return angle(vec2(CharacterDisplayInfo->m_TargetX, CharacterDisplayInfo->m_TargetY));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the player moves their weapon through top, then change
|
||||
// the end angle by 2*Pi, so that the mix function will use the
|
||||
// short path and not the long one.
|
||||
if(pPlayerChar->m_Angle > (256.0f * pi) && pPrevChar->m_Angle < 0)
|
||||
{
|
||||
return mix((float)pPrevChar->m_Angle, (float)(pPlayerChar->m_Angle - 256.0f * 2 * pi), AngleIntraTick) / 256.0f;
|
||||
}
|
||||
else if(pPlayerChar->m_Angle < 0 && pPrevChar->m_Angle > (256.0f * pi))
|
||||
{
|
||||
return mix((float)pPrevChar->m_Angle, (float)(pPlayerChar->m_Angle + 256.0f * 2 * pi), AngleIntraTick) / 256.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mix((float)pPrevChar->m_Angle, (float)pPlayerChar->m_Angle, AngleIntraTick) / 256.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayers::RenderHookCollLine(
|
||||
const CNetObj_Character *pPrevChar,
|
||||
const CNetObj_Character *pPlayerChar,
|
||||
|
@ -104,19 +152,7 @@ void CPlayers::RenderHookCollLine(
|
|||
}
|
||||
else
|
||||
{
|
||||
float AngleIntraTick = IntraTick;
|
||||
// using unpredicted angle when rendering other players in-game
|
||||
if(ClientID >= 0)
|
||||
AngleIntraTick = Client()->IntraGameTick(g_Config.m_ClDummy);
|
||||
// If the player moves their weapon through top, then change
|
||||
// the end angle by 2*Pi, so that the mix function will use the
|
||||
// short path and not the long one.
|
||||
if(Player.m_Angle > (256.0f * pi) && Prev.m_Angle < 0)
|
||||
Player.m_Angle -= 256.0f * 2 * pi;
|
||||
else if(Player.m_Angle < 0 && Prev.m_Angle > (256.0f * pi))
|
||||
Player.m_Angle += 256.0f * 2 * pi;
|
||||
|
||||
Angle = mix((float)Prev.m_Angle, (float)Player.m_Angle, AngleIntraTick) / 256.0f;
|
||||
Angle = GetPlayerTargetAngle(&Prev, &Player, ClientID, IntraTick);
|
||||
}
|
||||
|
||||
vec2 Direction = direction(Angle);
|
||||
|
@ -365,19 +401,7 @@ void CPlayers::RenderPlayer(
|
|||
}
|
||||
else
|
||||
{
|
||||
float AngleIntraTick = IntraTick;
|
||||
// using unpredicted angle when rendering other players in-game
|
||||
if(ClientID >= 0)
|
||||
AngleIntraTick = Client()->IntraGameTick(g_Config.m_ClDummy);
|
||||
// If the player moves their weapon through top, then change
|
||||
// the end angle by 2*Pi, so that the mix function will use the
|
||||
// short path and not the long one.
|
||||
if(Player.m_Angle > (256.0f * pi) && Prev.m_Angle < 0)
|
||||
Player.m_Angle -= 256.0f * 2 * pi;
|
||||
else if(Player.m_Angle < 0 && Prev.m_Angle > (256.0f * pi))
|
||||
Player.m_Angle += 256.0f * 2 * pi;
|
||||
|
||||
Angle = mix((float)Prev.m_Angle, (float)Player.m_Angle, AngleIntraTick) / 256.0f;
|
||||
Angle = GetPlayerTargetAngle(&Prev, &Player, ClientID, IntraTick);
|
||||
}
|
||||
|
||||
vec2 Direction = direction(Angle);
|
||||
|
|
|
@ -31,13 +31,17 @@ class CPlayers : public CComponent
|
|||
const CNetObj_Character *pPlayerChar,
|
||||
int ClientID,
|
||||
float Intra = 0.f);
|
||||
float GetPlayerTargetAngle(
|
||||
const CNetObj_Character *pPrevChar,
|
||||
const CNetObj_Character *pPlayerChar,
|
||||
int ClientID,
|
||||
float Intra = 0.f);
|
||||
bool IsPlayerInfoAvailable(int ClientID) const;
|
||||
|
||||
int m_WeaponEmoteQuadContainerIndex;
|
||||
int m_WeaponSpriteMuzzleQuadContainerIndex[NUM_WEAPONS];
|
||||
|
||||
public:
|
||||
vec2 m_CurPredictedPos[MAX_CLIENTS];
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
virtual void OnInit() override;
|
||||
virtual void OnRender() override;
|
||||
|
|
|
@ -693,7 +693,7 @@ void CScoreboard::OnRender()
|
|||
|
||||
RenderGoals(Width / 2 - w / 2, 150 + 760 + 10, w);
|
||||
RenderSpectators(Width / 2 - w / 2, 150 + 760 + 10 + 50 + 10, w);
|
||||
RenderRecordingNotification((Width / 7) * 4);
|
||||
RenderRecordingNotification((Width / 7) * 4 + 20);
|
||||
}
|
||||
|
||||
bool CScoreboard::Active()
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "components/effects.h"
|
||||
#include "components/emoticon.h"
|
||||
#include "components/flow.h"
|
||||
#include "components/freezebars.h"
|
||||
#include "components/hud.h"
|
||||
#include "components/items.h"
|
||||
#include "components/killmessages.h"
|
||||
|
@ -125,6 +126,7 @@ void CGameClient::OnConsoleInit()
|
|||
m_All.Add(&m_Particles.m_RenderExplosions);
|
||||
m_All.Add(&m_NamePlates);
|
||||
m_All.Add(&m_Particles.m_RenderGeneral);
|
||||
m_All.Add(&m_FreezeBars);
|
||||
m_All.Add(&m_DamageInd);
|
||||
m_All.Add(&m_Hud);
|
||||
m_All.Add(&m_Spectator);
|
||||
|
@ -262,6 +264,7 @@ void CGameClient::OnInit()
|
|||
m_GameSkinLoaded = false;
|
||||
m_ParticlesSkinLoaded = false;
|
||||
m_EmoticonsSkinLoaded = false;
|
||||
m_HudSkinLoaded = false;
|
||||
|
||||
// setup load amount// load textures
|
||||
for(int i = 0; i < g_pData->m_NumImages; i++)
|
||||
|
@ -272,6 +275,8 @@ void CGameClient::OnInit()
|
|||
LoadEmoticonsSkin(g_Config.m_ClAssetEmoticons);
|
||||
else if(i == IMAGE_PARTICLES)
|
||||
LoadParticlesSkin(g_Config.m_ClAssetParticles);
|
||||
else if(i == IMAGE_HUD)
|
||||
LoadHudSkin(g_Config.m_ClAssetHud);
|
||||
else
|
||||
g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
|
||||
m_Menus.RenderLoading(false);
|
||||
|
@ -1095,8 +1100,7 @@ void CGameClient::OnNewSnapshot()
|
|||
mem_zero(&TempCore, sizeof(TempCore));
|
||||
mem_zero(&TempTeams, sizeof(TempTeams));
|
||||
TempCore.Init(&TempWorld, Collision(), &TempTeams);
|
||||
TempCore.Read(pCharacter);
|
||||
TempCore.m_ActiveWeapon = pCharacter->m_Weapon;
|
||||
TempCore.ReadCharacter(pCharacter);
|
||||
|
||||
while(pCharacter->m_Tick < Tick)
|
||||
{
|
||||
|
@ -1271,6 +1275,7 @@ void CGameClient::OnNewSnapshot()
|
|||
{
|
||||
const void *pOld = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_CHARACTER, Item.m_ID);
|
||||
m_Snap.m_aCharacters[Item.m_ID].m_Cur = *((const CNetObj_Character *)pData);
|
||||
m_aClients[Item.m_ID].m_Predicted.ReadCharacter((const CNetObj_Character *)pData);
|
||||
if(pOld)
|
||||
{
|
||||
m_Snap.m_aCharacters[Item.m_ID].m_Active = true;
|
||||
|
@ -1341,6 +1346,20 @@ void CGameClient::OnNewSnapshot()
|
|||
pClient->m_Predicted.ReadDDNet(pCharacterData);
|
||||
}
|
||||
}
|
||||
else if(Item.m_Type == NETOBJTYPE_DDNETCHARACTERDISPLAYINFO)
|
||||
{
|
||||
const CNetObj_DDNetCharacterDisplayInfo *pCharacterDisplayInfo = (const CNetObj_DDNetCharacterDisplayInfo *)pData;
|
||||
|
||||
if(Item.m_ID < MAX_CLIENTS)
|
||||
{
|
||||
m_Snap.m_aCharacters[Item.m_ID].m_ExtendedDisplayInfo = *pCharacterDisplayInfo;
|
||||
m_Snap.m_aCharacters[Item.m_ID].m_PrevExtendedDisplayInfo = (const CNetObj_DDNetCharacterDisplayInfo *)Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_DDNETCHARACTERDISPLAYINFO, Item.m_ID);
|
||||
m_Snap.m_aCharacters[Item.m_ID].m_HasExtendedDisplayInfo = true;
|
||||
|
||||
CClientData *pClient = &m_aClients[Item.m_ID];
|
||||
pClient->m_Predicted.ReadDDNetDisplayInfo(pCharacterDisplayInfo);
|
||||
}
|
||||
}
|
||||
else if(Item.m_Type == NETOBJTYPE_SPECCHAR)
|
||||
{
|
||||
const CNetObj_SpecChar *pSpecCharData = (const CNetObj_SpecChar *)pData;
|
||||
|
@ -1705,13 +1724,11 @@ void CGameClient::OnPredict()
|
|||
{
|
||||
if(m_Snap.m_pLocalCharacter)
|
||||
{
|
||||
m_PredictedChar.Read(m_Snap.m_pLocalCharacter);
|
||||
m_PredictedChar.m_ActiveWeapon = m_Snap.m_pLocalCharacter->m_Weapon;
|
||||
m_PredictedChar.ReadCharacter(m_Snap.m_pLocalCharacter);
|
||||
}
|
||||
if(m_Snap.m_pLocalPrevCharacter)
|
||||
{
|
||||
m_PredictedPrevChar.Read(m_Snap.m_pLocalPrevCharacter);
|
||||
m_PredictedPrevChar.m_ActiveWeapon = m_Snap.m_pLocalPrevCharacter->m_Weapon;
|
||||
m_PredictedPrevChar.ReadCharacter(m_Snap.m_pLocalPrevCharacter);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -2418,6 +2435,7 @@ void CGameClient::UpdatePrediction()
|
|||
int GameTeam = (m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) ? m_aClients[i].m_Team : i;
|
||||
m_GameWorld.NetCharAdd(i, &m_Snap.m_aCharacters[i].m_Cur,
|
||||
m_Snap.m_aCharacters[i].m_HasExtendedData ? &m_Snap.m_aCharacters[i].m_ExtendedData : 0,
|
||||
m_Snap.m_aCharacters[i].m_HasExtendedDisplayInfo ? &m_Snap.m_aCharacters[i].m_ExtendedDisplayInfo : 0,
|
||||
GameTeam, IsLocal);
|
||||
}
|
||||
|
||||
|
@ -2516,7 +2534,7 @@ void CGameClient::DetectStrongHook()
|
|||
|
||||
float PredictErr[2];
|
||||
CCharacterCore ToCharCur;
|
||||
ToCharCur.Read(&m_Snap.m_aCharacters[ToPlayer].m_Cur);
|
||||
ToCharCur.ReadCharacterCore(&m_Snap.m_aCharacters[ToPlayer].m_Cur);
|
||||
|
||||
CWorldCore World;
|
||||
World.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
|
||||
|
@ -2526,12 +2544,12 @@ void CGameClient::DetectStrongHook()
|
|||
CCharacterCore ToChar = pFromCharWorld->GetCore();
|
||||
ToChar.Init(&World, Collision(), &m_Teams);
|
||||
World.m_apCharacters[ToPlayer] = &ToChar;
|
||||
ToChar.Read(&m_Snap.m_aCharacters[ToPlayer].m_Prev);
|
||||
ToChar.ReadCharacterCore(&m_Snap.m_aCharacters[ToPlayer].m_Prev);
|
||||
|
||||
CCharacterCore FromChar = pFromCharWorld->GetCore();
|
||||
FromChar.Init(&World, Collision(), &m_Teams);
|
||||
World.m_apCharacters[FromPlayer] = &FromChar;
|
||||
FromChar.Read(&m_Snap.m_aCharacters[FromPlayer].m_Prev);
|
||||
FromChar.ReadCharacterCore(&m_Snap.m_aCharacters[FromPlayer].m_Prev);
|
||||
|
||||
for(int Tick = Client()->PrevGameTick(g_Config.m_ClDummy); Tick < Client()->GameTick(g_Config.m_ClDummy); Tick++)
|
||||
{
|
||||
|
@ -2731,6 +2749,11 @@ void CGameClient::LoadGameSkin(const char *pPath, bool AsDir)
|
|||
SpritePickupWeapon = IGraphics::CTextureHandle();
|
||||
}
|
||||
|
||||
for(auto &SpritePickupWeaponArmor : m_GameSkin.m_SpritePickupWeaponArmor)
|
||||
{
|
||||
SpritePickupWeaponArmor = IGraphics::CTextureHandle();
|
||||
}
|
||||
|
||||
Graphics()->UnloadTexture(&m_GameSkin.m_SpriteFlagBlue);
|
||||
Graphics()->UnloadTexture(&m_GameSkin.m_SpriteFlagRed);
|
||||
|
||||
|
@ -2852,16 +2875,16 @@ void CGameClient::LoadGameSkin(const char *pPath, bool AsDir)
|
|||
// pickups
|
||||
m_GameSkin.m_SpritePickupHealth = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_HEALTH]);
|
||||
m_GameSkin.m_SpritePickupArmor = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR]);
|
||||
m_GameSkin.m_SpritePickupHammer = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_HAMMER]);
|
||||
m_GameSkin.m_SpritePickupGun = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_GUN]);
|
||||
m_GameSkin.m_SpritePickupShotgun = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_SHOTGUN]);
|
||||
m_GameSkin.m_SpritePickupGrenade = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_GRENADE]);
|
||||
m_GameSkin.m_SpritePickupLaser = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_LASER]);
|
||||
m_GameSkin.m_SpritePickupNinja = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_NINJA]);
|
||||
m_GameSkin.m_SpritePickupArmorShotgun = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR_SHOTGUN]);
|
||||
m_GameSkin.m_SpritePickupArmorGrenade = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR_GRENADE]);
|
||||
m_GameSkin.m_SpritePickupArmorLaser = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR_LASER]);
|
||||
m_GameSkin.m_SpritePickupArmorNinja = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR_NINJA]);
|
||||
m_GameSkin.m_SpritePickupGrenade = Graphics()->LoadSpriteTexture(ImgInfo, &client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_GRENADE]);
|
||||
m_GameSkin.m_SpritePickupShotgun = Graphics()->LoadSpriteTexture(ImgInfo, &client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_SHOTGUN]);
|
||||
m_GameSkin.m_SpritePickupLaser = Graphics()->LoadSpriteTexture(ImgInfo, &client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_LASER]);
|
||||
m_GameSkin.m_SpritePickupNinja = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_NINJA]);
|
||||
m_GameSkin.m_SpritePickupGun = Graphics()->LoadSpriteTexture(ImgInfo, &client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_GUN]);
|
||||
m_GameSkin.m_SpritePickupHammer = Graphics()->LoadSpriteTexture(ImgInfo, &client_data7::g_pData->m_aSprites[client_data7::SPRITE_PICKUP_HAMMER]);
|
||||
m_GameSkin.m_SpritePickupArmorLaser = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PICKUP_ARMOR_LASER]);
|
||||
|
||||
m_GameSkin.m_SpritePickupWeapons[0] = m_GameSkin.m_SpritePickupHammer;
|
||||
m_GameSkin.m_SpritePickupWeapons[1] = m_GameSkin.m_SpritePickupGun;
|
||||
|
@ -2870,6 +2893,11 @@ void CGameClient::LoadGameSkin(const char *pPath, bool AsDir)
|
|||
m_GameSkin.m_SpritePickupWeapons[4] = m_GameSkin.m_SpritePickupLaser;
|
||||
m_GameSkin.m_SpritePickupWeapons[5] = m_GameSkin.m_SpritePickupNinja;
|
||||
|
||||
m_GameSkin.m_SpritePickupWeaponArmor[0] = m_GameSkin.m_SpritePickupArmorShotgun;
|
||||
m_GameSkin.m_SpritePickupWeaponArmor[1] = m_GameSkin.m_SpritePickupArmorGrenade;
|
||||
m_GameSkin.m_SpritePickupWeaponArmor[2] = m_GameSkin.m_SpritePickupArmorNinja;
|
||||
m_GameSkin.m_SpritePickupWeaponArmor[3] = m_GameSkin.m_SpritePickupArmorLaser;
|
||||
|
||||
// flags
|
||||
m_GameSkin.m_SpriteFlagBlue = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_FLAG_BLUE]);
|
||||
m_GameSkin.m_SpriteFlagRed = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_FLAG_RED]);
|
||||
|
@ -3007,6 +3035,101 @@ void CGameClient::LoadParticlesSkin(const char *pPath, bool AsDir)
|
|||
}
|
||||
}
|
||||
|
||||
void CGameClient::LoadHudSkin(const char *pPath, bool AsDir)
|
||||
{
|
||||
if(m_HudSkinLoaded)
|
||||
{
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudAirjump);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudAirjumpEmpty);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudSolo);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoCollision);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudEndlessJump);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudEndlessHook);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudJetpack);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudFreezeBarFullLeft);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudFreezeBarFull);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudFreezeBarEmpty);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudFreezeBarEmptyRight);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNinjaBarFullLeft);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNinjaBarFull);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNinjaBarEmpty);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNinjaBarEmptyRight);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoHookHit);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoHammerHit);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoShotgunHit);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoGrenadeHit);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudNoLaserHit);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudDeepFrozen);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudLiveFrozen);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudTeleportGrenade);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudTeleportGun);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudTeleportLaser);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudPracticeMode);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudDummyHammer);
|
||||
Graphics()->UnloadTexture(&m_HudSkin.m_SpriteHudDummyCopy);
|
||||
m_HudSkinLoaded = false;
|
||||
}
|
||||
|
||||
char aPath[IO_MAX_PATH_LENGTH];
|
||||
bool IsDefault = false;
|
||||
if(str_comp(pPath, "default") == 0)
|
||||
{
|
||||
str_format(aPath, sizeof(aPath), "%s", g_pData->m_aImages[IMAGE_HUD].m_pFilename);
|
||||
IsDefault = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(AsDir)
|
||||
str_format(aPath, sizeof(aPath), "assets/hud/%s/%s", pPath, g_pData->m_aImages[IMAGE_HUD].m_pFilename);
|
||||
else
|
||||
str_format(aPath, sizeof(aPath), "assets/hud/%s.png", pPath);
|
||||
}
|
||||
|
||||
CImageInfo ImgInfo;
|
||||
bool PngLoaded = Graphics()->LoadPNG(&ImgInfo, aPath, IStorage::TYPE_ALL);
|
||||
if(!PngLoaded && !IsDefault)
|
||||
{
|
||||
if(AsDir)
|
||||
LoadHudSkin("default");
|
||||
else
|
||||
LoadHudSkin(pPath, true);
|
||||
}
|
||||
else if(PngLoaded && Graphics()->CheckImageDivisibility(aPath, ImgInfo, g_pData->m_aSprites[SPRITE_HUD_AIRJUMP].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_HUD_AIRJUMP].m_pSet->m_Gridy, true) && Graphics()->IsImageFormatRGBA(aPath, ImgInfo))
|
||||
{
|
||||
m_HudSkin.m_SpriteHudAirjump = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_AIRJUMP]);
|
||||
m_HudSkin.m_SpriteHudAirjumpEmpty = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_AIRJUMP_EMPTY]);
|
||||
m_HudSkin.m_SpriteHudSolo = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_SOLO]);
|
||||
m_HudSkin.m_SpriteHudNoCollision = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_COLLISION]);
|
||||
m_HudSkin.m_SpriteHudEndlessJump = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_ENDLESS_JUMP]);
|
||||
m_HudSkin.m_SpriteHudEndlessHook = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_ENDLESS_HOOK]);
|
||||
m_HudSkin.m_SpriteHudJetpack = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_JETPACK]);
|
||||
m_HudSkin.m_SpriteHudFreezeBarFullLeft = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_FREEZE_BAR_FULL_LEFT]);
|
||||
m_HudSkin.m_SpriteHudFreezeBarFull = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_FREEZE_BAR_FULL]);
|
||||
m_HudSkin.m_SpriteHudFreezeBarEmpty = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_FREEZE_BAR_EMPTY]);
|
||||
m_HudSkin.m_SpriteHudFreezeBarEmptyRight = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_FREEZE_BAR_EMPTY_RIGHT]);
|
||||
m_HudSkin.m_SpriteHudNinjaBarFullLeft = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NINJA_BAR_FULL_LEFT]);
|
||||
m_HudSkin.m_SpriteHudNinjaBarFull = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NINJA_BAR_FULL]);
|
||||
m_HudSkin.m_SpriteHudNinjaBarEmpty = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NINJA_BAR_EMPTY]);
|
||||
m_HudSkin.m_SpriteHudNinjaBarEmptyRight = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NINJA_BAR_EMPTY_RIGHT]);
|
||||
m_HudSkin.m_SpriteHudNoHookHit = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_HOOK_HIT]);
|
||||
m_HudSkin.m_SpriteHudNoHammerHit = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_HAMMER_HIT]);
|
||||
m_HudSkin.m_SpriteHudNoShotgunHit = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_SHOTGUN_HIT]);
|
||||
m_HudSkin.m_SpriteHudNoGrenadeHit = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_GRENADE_HIT]);
|
||||
m_HudSkin.m_SpriteHudNoLaserHit = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_NO_LASER_HIT]);
|
||||
m_HudSkin.m_SpriteHudDeepFrozen = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_DEEP_FROZEN]);
|
||||
m_HudSkin.m_SpriteHudLiveFrozen = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_LIVE_FROZEN]);
|
||||
m_HudSkin.m_SpriteHudTeleportGrenade = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_TELEPORT_GRENADE]);
|
||||
m_HudSkin.m_SpriteHudTeleportGun = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_TELEPORT_GUN]);
|
||||
m_HudSkin.m_SpriteHudTeleportLaser = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_TELEPORT_LASER]);
|
||||
m_HudSkin.m_SpriteHudPracticeMode = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_PRACTICE_MODE]);
|
||||
m_HudSkin.m_SpriteHudDummyHammer = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_DUMMY_HAMMER]);
|
||||
m_HudSkin.m_SpriteHudDummyCopy = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HUD_DUMMY_COPY]);
|
||||
|
||||
m_HudSkinLoaded = true;
|
||||
free(ImgInfo.m_pData);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameClient::RefindSkins()
|
||||
{
|
||||
for(auto &Client : m_aClients)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "components/effects.h"
|
||||
#include "components/emoticon.h"
|
||||
#include "components/flow.h"
|
||||
#include "components/freezebars.h"
|
||||
#include "components/ghost.h"
|
||||
#include "components/hud.h"
|
||||
#include "components/items.h"
|
||||
|
@ -132,6 +133,7 @@ public:
|
|||
|
||||
CPlayers m_Players;
|
||||
CNamePlates m_NamePlates;
|
||||
CFreezeBars m_FreezeBars;
|
||||
CItems m_Items;
|
||||
CMapImages m_MapImages;
|
||||
|
||||
|
@ -319,6 +321,10 @@ public:
|
|||
CNetObj_DDNetCharacter m_ExtendedData;
|
||||
bool m_HasExtendedData;
|
||||
|
||||
const CNetObj_DDNetCharacterDisplayInfo *m_PrevExtendedDisplayInfo;
|
||||
CNetObj_DDNetCharacterDisplayInfo m_ExtendedDisplayInfo;
|
||||
bool m_HasExtendedDisplayInfo;
|
||||
|
||||
// interpolated position
|
||||
vec2 m_Position;
|
||||
};
|
||||
|
@ -533,6 +539,7 @@ public:
|
|||
void LoadGameSkin(const char *pPath, bool AsDir = false);
|
||||
void LoadEmoticonsSkin(const char *pPath, bool AsDir = false);
|
||||
void LoadParticlesSkin(const char *pPath, bool AsDir = false);
|
||||
void LoadHudSkin(const char *pPath, bool AsDir = false);
|
||||
|
||||
void RefindSkins();
|
||||
|
||||
|
@ -594,8 +601,8 @@ public:
|
|||
IGraphics::CTextureHandle m_SpritePickupArmor;
|
||||
IGraphics::CTextureHandle m_SpritePickupArmorShotgun;
|
||||
IGraphics::CTextureHandle m_SpritePickupArmorGrenade;
|
||||
IGraphics::CTextureHandle m_SpritePickupArmorLaser;
|
||||
IGraphics::CTextureHandle m_SpritePickupArmorNinja;
|
||||
IGraphics::CTextureHandle m_SpritePickupArmorLaser;
|
||||
IGraphics::CTextureHandle m_SpritePickupGrenade;
|
||||
IGraphics::CTextureHandle m_SpritePickupShotgun;
|
||||
IGraphics::CTextureHandle m_SpritePickupLaser;
|
||||
|
@ -604,6 +611,7 @@ public:
|
|||
IGraphics::CTextureHandle m_SpritePickupHammer;
|
||||
|
||||
IGraphics::CTextureHandle m_SpritePickupWeapons[6];
|
||||
IGraphics::CTextureHandle m_SpritePickupWeaponArmor[4];
|
||||
|
||||
// flags
|
||||
IGraphics::CTextureHandle m_SpriteFlagBlue;
|
||||
|
@ -648,6 +656,41 @@ public:
|
|||
SClientEmoticonsSkin m_EmoticonsSkin;
|
||||
bool m_EmoticonsSkinLoaded;
|
||||
|
||||
struct SClientHudSkin
|
||||
{
|
||||
IGraphics::CTextureHandle m_SpriteHudAirjump;
|
||||
IGraphics::CTextureHandle m_SpriteHudAirjumpEmpty;
|
||||
IGraphics::CTextureHandle m_SpriteHudSolo;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoCollision;
|
||||
IGraphics::CTextureHandle m_SpriteHudEndlessJump;
|
||||
IGraphics::CTextureHandle m_SpriteHudEndlessHook;
|
||||
IGraphics::CTextureHandle m_SpriteHudJetpack;
|
||||
IGraphics::CTextureHandle m_SpriteHudFreezeBarFullLeft;
|
||||
IGraphics::CTextureHandle m_SpriteHudFreezeBarFull;
|
||||
IGraphics::CTextureHandle m_SpriteHudFreezeBarEmpty;
|
||||
IGraphics::CTextureHandle m_SpriteHudFreezeBarEmptyRight;
|
||||
IGraphics::CTextureHandle m_SpriteHudNinjaBarFullLeft;
|
||||
IGraphics::CTextureHandle m_SpriteHudNinjaBarFull;
|
||||
IGraphics::CTextureHandle m_SpriteHudNinjaBarEmpty;
|
||||
IGraphics::CTextureHandle m_SpriteHudNinjaBarEmptyRight;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoHookHit;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoHammerHit;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoShotgunHit;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoGrenadeHit;
|
||||
IGraphics::CTextureHandle m_SpriteHudNoLaserHit;
|
||||
IGraphics::CTextureHandle m_SpriteHudDeepFrozen;
|
||||
IGraphics::CTextureHandle m_SpriteHudLiveFrozen;
|
||||
IGraphics::CTextureHandle m_SpriteHudTeleportGrenade;
|
||||
IGraphics::CTextureHandle m_SpriteHudTeleportGun;
|
||||
IGraphics::CTextureHandle m_SpriteHudTeleportLaser;
|
||||
IGraphics::CTextureHandle m_SpriteHudPracticeMode;
|
||||
IGraphics::CTextureHandle m_SpriteHudDummyHammer;
|
||||
IGraphics::CTextureHandle m_SpriteHudDummyCopy;
|
||||
};
|
||||
|
||||
SClientHudSkin m_HudSkin;
|
||||
bool m_HudSkinLoaded;
|
||||
|
||||
const std::vector<CSnapEntities> &SnapEntities() { return m_aSnapEntities; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -58,14 +58,14 @@ void CCharacter::HandleJetpack()
|
|||
if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses)
|
||||
WillFire = true;
|
||||
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
WillFire = true;
|
||||
|
||||
if(!WillFire)
|
||||
return;
|
||||
|
||||
// check for ammo
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -87,8 +87,8 @@ void CCharacter::HandleJetpack()
|
|||
|
||||
void CCharacter::RemoveNinja()
|
||||
{
|
||||
m_Ninja.m_CurrentMoveTime = 0;
|
||||
m_aWeapons[WEAPON_NINJA].m_Got = false;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime = 0;
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Got = false;
|
||||
m_Core.m_ActiveWeapon = m_LastWeapon;
|
||||
|
||||
SetWeapon(m_Core.m_ActiveWeapon);
|
||||
|
@ -99,7 +99,7 @@ void CCharacter::HandleNinja()
|
|||
if(m_Core.m_ActiveWeapon != WEAPON_NINJA)
|
||||
return;
|
||||
|
||||
if((GameWorld()->GameTick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * GameWorld()->GameTickSpeed() / 1000))
|
||||
if((GameWorld()->GameTick() - m_Core.m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * GameWorld()->GameTickSpeed() / 1000))
|
||||
{
|
||||
// time's up, return
|
||||
RemoveNinja();
|
||||
|
@ -109,18 +109,18 @@ void CCharacter::HandleNinja()
|
|||
// force ninja Weapon
|
||||
SetWeapon(WEAPON_NINJA);
|
||||
|
||||
m_Ninja.m_CurrentMoveTime--;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime--;
|
||||
|
||||
if(m_Ninja.m_CurrentMoveTime == 0)
|
||||
if(m_Core.m_Ninja.m_CurrentMoveTime == 0)
|
||||
{
|
||||
// reset velocity
|
||||
m_Core.m_Vel = m_Ninja.m_ActivationDir * m_Ninja.m_OldVelAmount;
|
||||
m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * m_Core.m_Ninja.m_OldVelAmount;
|
||||
}
|
||||
|
||||
if(m_Ninja.m_CurrentMoveTime > 0)
|
||||
if(m_Core.m_Ninja.m_CurrentMoveTime > 0)
|
||||
{
|
||||
// Set velocity
|
||||
m_Core.m_Vel = m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity;
|
||||
m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity;
|
||||
vec2 OldPos = m_Pos;
|
||||
Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(m_ProximityRadius, m_ProximityRadius), 0.f);
|
||||
|
||||
|
@ -184,7 +184,7 @@ void CCharacter::HandleNinja()
|
|||
void CCharacter::DoWeaponSwitch()
|
||||
{
|
||||
// make sure we can switch
|
||||
if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_aWeapons[WEAPON_NINJA].m_Got || !m_aWeapons[m_QueuedWeapon].m_Got)
|
||||
if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_Core.m_aWeapons[WEAPON_NINJA].m_Got || !m_Core.m_aWeapons[m_QueuedWeapon].m_Got)
|
||||
return;
|
||||
|
||||
// switch Weapon
|
||||
|
@ -202,7 +202,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
|
||||
bool Anything = false;
|
||||
for(int i = 0; i < NUM_WEAPONS - 1; ++i)
|
||||
if(m_aWeapons[i].m_Got)
|
||||
if(m_Core.m_aWeapons[i].m_Got)
|
||||
Anything = true;
|
||||
if(!Anything)
|
||||
return;
|
||||
|
@ -215,7 +215,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
while(Next) // Next Weapon selection
|
||||
{
|
||||
WantedWeapon = (WantedWeapon + 1) % NUM_WEAPONS;
|
||||
if(m_aWeapons[WantedWeapon].m_Got)
|
||||
if(m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
Next--;
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
while(Prev) // Prev Weapon selection
|
||||
{
|
||||
WantedWeapon = (WantedWeapon - 1) < 0 ? NUM_WEAPONS - 1 : WantedWeapon - 1;
|
||||
if(m_aWeapons[WantedWeapon].m_Got)
|
||||
if(m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
Prev--;
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
WantedWeapon = m_Input.m_WantedWeapon - 1;
|
||||
|
||||
// check for insane values
|
||||
if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_Core.m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got)
|
||||
if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_Core.m_ActiveWeapon && m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
m_QueuedWeapon = WantedWeapon;
|
||||
|
||||
DoWeaponSwitch();
|
||||
|
@ -272,14 +272,14 @@ void CCharacter::FireWeapon()
|
|||
if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses)
|
||||
WillFire = true;
|
||||
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
WillFire = true;
|
||||
|
||||
if(!WillFire)
|
||||
return;
|
||||
|
||||
// check for ammo
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -445,9 +445,9 @@ void CCharacter::FireWeapon()
|
|||
// reset Hit objects
|
||||
m_NumObjectsHit = 0;
|
||||
|
||||
m_Ninja.m_ActivationDir = Direction;
|
||||
m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * GameWorld()->GameTickSpeed() / 1000;
|
||||
m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
m_Core.m_Ninja.m_ActivationDir = Direction;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * GameWorld()->GameTickSpeed() / 1000;
|
||||
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -482,10 +482,10 @@ void CCharacter::HandleWeapons()
|
|||
|
||||
void CCharacter::GiveNinja()
|
||||
{
|
||||
m_Ninja.m_ActivationTick = GameWorld()->GameTick();
|
||||
m_aWeapons[WEAPON_NINJA].m_Got = true;
|
||||
m_Core.m_Ninja.m_ActivationTick = GameWorld()->GameTick();
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Got = true;
|
||||
if(!m_FreezeTime)
|
||||
m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
|
||||
if(m_Core.m_ActiveWeapon != WEAPON_NINJA)
|
||||
m_LastWeapon = m_Core.m_ActiveWeapon;
|
||||
m_Core.m_ActiveWeapon = WEAPON_NINJA;
|
||||
|
@ -697,8 +697,10 @@ void CCharacter::HandleTiles(int Index)
|
|||
if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER)
|
||||
{
|
||||
if(Collision()->GetSwitchNumber(MapIndex) == 0 || Collision()->m_pSwitchers[Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()])
|
||||
{
|
||||
Freeze(Collision()->GetSwitchDelay(MapIndex));
|
||||
}
|
||||
}
|
||||
else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER)
|
||||
{
|
||||
if(Collision()->GetSwitchNumber(MapIndex) == 0 || Collision()->m_pSwitchers[Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()])
|
||||
|
@ -859,7 +861,7 @@ void CCharacter::HandleTiles(int Index)
|
|||
if(m_Core.m_Vel.y > 0 && m_Core.m_Colliding && m_Core.m_LeftWall)
|
||||
{
|
||||
m_Core.m_LeftWall = false;
|
||||
m_Core.m_JumpedTotal = m_Core.m_Jumps - 1;
|
||||
m_Core.m_JumpedTotal = m_Core.m_Jumps >= 2 ? m_Core.m_Jumps - 2 : 0;
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
}
|
||||
|
@ -923,7 +925,7 @@ void CCharacter::DDRaceTick()
|
|||
if(m_FreezeTime > 0)
|
||||
m_FreezeTime--;
|
||||
else
|
||||
m_Ninja.m_ActivationTick = GameWorld()->GameTick();
|
||||
m_Core.m_Ninja.m_ActivationTick = GameWorld()->GameTick();
|
||||
if(!m_CanMoveInFreeze)
|
||||
{
|
||||
m_Input.m_Direction = 0;
|
||||
|
@ -935,6 +937,22 @@ void CCharacter::DDRaceTick()
|
|||
}
|
||||
|
||||
HandleTuneLayer();
|
||||
|
||||
// check if the tee is in any type of freeze
|
||||
int Index = Collision()->GetPureMapIndex(m_Pos);
|
||||
const int aTiles[] = {
|
||||
Collision()->GetTileIndex(Index),
|
||||
Collision()->GetFTileIndex(Index),
|
||||
Collision()->GetSwitchType(Index)};
|
||||
m_Core.m_IsInFreeze = false;
|
||||
for(const int Tile : aTiles)
|
||||
{
|
||||
if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE)
|
||||
{
|
||||
m_Core.m_IsInFreeze = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCharacter::DDRacePostCoreTick()
|
||||
|
@ -950,17 +968,32 @@ void CCharacter::DDRacePostCoreTick()
|
|||
if(m_DeepFreeze && !m_Super)
|
||||
Freeze();
|
||||
|
||||
if(m_Core.m_Jumps == -1 && !m_Super)
|
||||
if(m_Core.m_Jumps == -1)
|
||||
{
|
||||
// The player has only one ground jump, so his feet are always dark
|
||||
m_Core.m_Jumped |= 2;
|
||||
else if(m_Core.m_Jumps == 0 && !m_Super)
|
||||
m_Core.m_Jumped = 3;
|
||||
}
|
||||
else if(m_Core.m_Jumps == 0)
|
||||
{
|
||||
// The player has no jumps at all, so his feet are always dark
|
||||
m_Core.m_Jumped |= 2;
|
||||
}
|
||||
else if(m_Core.m_Jumps == 1 && m_Core.m_Jumped > 0)
|
||||
m_Core.m_Jumped = 3;
|
||||
{
|
||||
// If the player has only one jump, each jump is the last one
|
||||
m_Core.m_Jumped |= 2;
|
||||
}
|
||||
else if(m_Core.m_JumpedTotal < m_Core.m_Jumps - 1 && m_Core.m_Jumped > 1)
|
||||
{
|
||||
// The player has not yet used up all his jumps, so his feet remain light
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
|
||||
if((m_Super || m_SuperJump) && m_Core.m_Jumped > 1)
|
||||
{
|
||||
// Super players and players with infinite jumps always have light feet
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
|
||||
int CurrentIndex = Collision()->GetMapIndex(m_Pos);
|
||||
HandleSkippableTiles(CurrentIndex);
|
||||
|
@ -982,10 +1015,10 @@ bool CCharacter::Freeze(int Seconds)
|
|||
return false;
|
||||
if((Seconds <= 0 || m_Super || m_FreezeTime == -1 || m_FreezeTime > Seconds * GameWorld()->GameTickSpeed()) && Seconds != -1)
|
||||
return false;
|
||||
if(m_FreezeTick < GameWorld()->GameTick() - GameWorld()->GameTickSpeed() || Seconds == -1)
|
||||
if(m_Core.m_FreezeTick < GameWorld()->GameTick() - GameWorld()->GameTickSpeed() || Seconds == -1)
|
||||
{
|
||||
m_FreezeTime = Seconds == -1 ? Seconds : Seconds * GameWorld()->GameTickSpeed();
|
||||
m_FreezeTick = GameWorld()->GameTick();
|
||||
m_Core.m_FreezeTick = GameWorld()->GameTick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1000,10 +1033,10 @@ bool CCharacter::UnFreeze()
|
|||
{
|
||||
if(m_FreezeTime > 0)
|
||||
{
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Got)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Got)
|
||||
m_Core.m_ActiveWeapon = WEAPON_GUN;
|
||||
m_FreezeTime = 0;
|
||||
m_FreezeTick = 0;
|
||||
m_Core.m_FreezeTick = 0;
|
||||
m_FrozenLastTick = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -1028,10 +1061,10 @@ void CCharacter::GiveWeapon(int Weapon, bool Remove)
|
|||
}
|
||||
else
|
||||
{
|
||||
m_aWeapons[Weapon].m_Ammo = -1;
|
||||
m_Core.m_aWeapons[Weapon].m_Ammo = -1;
|
||||
}
|
||||
|
||||
m_aWeapons[Weapon].m_Got = !Remove;
|
||||
m_Core.m_aWeapons[Weapon].m_Got = !Remove;
|
||||
}
|
||||
|
||||
void CCharacter::GiveAllWeapons()
|
||||
|
@ -1047,7 +1080,7 @@ CTeamsCore *CCharacter::TeamsCore()
|
|||
return m_Core.m_pTeams;
|
||||
}
|
||||
|
||||
CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended) :
|
||||
CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_CHARACTER)
|
||||
{
|
||||
m_ID = ID;
|
||||
|
@ -1060,7 +1093,7 @@ CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar,
|
|||
m_Core.Reset();
|
||||
m_Core.Init(&GameWorld()->m_Core, GameWorld()->Collision(), GameWorld()->Teams());
|
||||
m_Core.m_Id = ID;
|
||||
mem_zero(&m_Ninja, sizeof(m_Ninja));
|
||||
mem_zero(&m_Core.m_Ninja, sizeof(m_Core.m_Ninja));
|
||||
mem_zero(&m_SavedInput, sizeof(m_SavedInput));
|
||||
m_LatestInput = m_LatestPrevInput = m_PrevInput = m_Input = m_SavedInput;
|
||||
m_ProximityRadius = ms_PhysSize;
|
||||
|
@ -1082,7 +1115,7 @@ CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar,
|
|||
m_LatestPrevInput = m_LatestInput = m_PrevInput = m_SavedInput = m_Input;
|
||||
|
||||
ResetPrediction();
|
||||
Read(pChar, pExtended, false);
|
||||
Read(pChar, pExtended, pExtendedDisplayInfo, false);
|
||||
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
@ -1100,7 +1133,8 @@ void CCharacter::ResetPrediction()
|
|||
m_Core.m_Collision = true;
|
||||
m_NumInputs = 0;
|
||||
m_FreezeTime = 0;
|
||||
m_FreezeTick = 0;
|
||||
m_Core.m_FreezeTick = 0;
|
||||
m_Core.m_IsInFreeze = false;
|
||||
m_DeepFreeze = false;
|
||||
m_LiveFreeze = false;
|
||||
m_FrozenLastTick = false;
|
||||
|
@ -1119,9 +1153,9 @@ void CCharacter::ResetPrediction()
|
|||
m_LastTuneZoneTick = 0;
|
||||
}
|
||||
|
||||
void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, bool IsLocal)
|
||||
void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo, bool IsLocal)
|
||||
{
|
||||
m_Core.Read((CNetObj_CharacterCore *)pChar);
|
||||
m_Core.ReadCharacterCore((const CNetObj_CharacterCore *)pChar);
|
||||
m_IsLocal = IsLocal;
|
||||
|
||||
if(pExtended)
|
||||
|
@ -1149,12 +1183,6 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
if(pExtended->m_Flags & CHARACTERFLAG_NO_SHOTGUN_HIT)
|
||||
m_Hit |= DISABLE_HIT_SHOTGUN;
|
||||
|
||||
m_aWeapons[WEAPON_HAMMER].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_HAMMER) != 0;
|
||||
m_aWeapons[WEAPON_GUN].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_GUN) != 0;
|
||||
m_aWeapons[WEAPON_SHOTGUN].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_SHOTGUN) != 0;
|
||||
m_aWeapons[WEAPON_GRENADE].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_GRENADE) != 0;
|
||||
m_aWeapons[WEAPON_LASER].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_LASER) != 0;
|
||||
|
||||
const bool Ninja = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_NINJA) != 0;
|
||||
if(Ninja && m_Core.m_ActiveWeapon != WEAPON_NINJA)
|
||||
GiveNinja();
|
||||
|
@ -1185,7 +1213,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
if(pChar->m_Weapon != m_Core.m_ActiveWeapon)
|
||||
{
|
||||
if(pChar->m_Weapon == WEAPON_NINJA)
|
||||
m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo = 0;
|
||||
m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo = 0;
|
||||
else
|
||||
{
|
||||
if(m_Core.m_ActiveWeapon == WEAPON_NINJA)
|
||||
|
@ -1195,12 +1223,12 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
SetNinjaCurrentMoveTime(0);
|
||||
}
|
||||
if(pChar->m_Weapon == m_LastSnapWeapon)
|
||||
m_aWeapons[m_Core.m_ActiveWeapon].m_Got = false;
|
||||
m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Got = false;
|
||||
}
|
||||
}
|
||||
// add weapon
|
||||
if(pChar->m_Weapon != WEAPON_NINJA)
|
||||
m_aWeapons[pChar->m_Weapon].m_Got = true;
|
||||
m_Core.m_aWeapons[pChar->m_Weapon].m_Got = true;
|
||||
|
||||
// jetpack
|
||||
if(GameWorld()->m_WorldConfig.m_PredictWeapons && Tuning()->m_JetpackStrength > 0)
|
||||
|
@ -1208,8 +1236,8 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
m_LastJetpackStrength = Tuning()->m_JetpackStrength;
|
||||
m_Jetpack = true;
|
||||
m_Core.m_Jetpack = true;
|
||||
m_aWeapons[WEAPON_GUN].m_Got = true;
|
||||
m_aWeapons[WEAPON_GUN].m_Ammo = -1;
|
||||
m_Core.m_aWeapons[WEAPON_GUN].m_Got = true;
|
||||
m_Core.m_aWeapons[WEAPON_GUN].m_Ammo = -1;
|
||||
m_NinjaJetpack = pChar->m_Weapon == WEAPON_NINJA;
|
||||
}
|
||||
else if(pChar->m_Weapon != WEAPON_NINJA)
|
||||
|
@ -1247,7 +1275,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
// detect unfreeze (in case the player was frozen in the tile prediction and not correctly unfrozen)
|
||||
if(pChar->m_Emote != EMOTE_PAIN && pChar->m_Emote != EMOTE_NORMAL)
|
||||
m_DeepFreeze = false;
|
||||
if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_FreezeTick || absolute(pChar->m_VelX) == 256 * 10 || !GameWorld()->m_WorldConfig.m_PredictFreeze)
|
||||
if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_Core.m_FreezeTick || absolute(pChar->m_VelX) == 256 * 10 || !GameWorld()->m_WorldConfig.m_PredictFreeze)
|
||||
{
|
||||
m_DeepFreeze = false;
|
||||
UnFreeze();
|
||||
|
@ -1274,7 +1302,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
// set the current weapon
|
||||
if(pChar->m_Weapon != WEAPON_NINJA)
|
||||
{
|
||||
m_aWeapons[pChar->m_Weapon].m_Ammo = (GameWorld()->m_WorldConfig.m_InfiniteAmmo || GameWorld()->m_WorldConfig.m_IsDDRace || pChar->m_Weapon == WEAPON_HAMMER) ? -1 : pChar->m_AmmoCount;
|
||||
m_Core.m_aWeapons[pChar->m_Weapon].m_Ammo = (GameWorld()->m_WorldConfig.m_InfiniteAmmo || GameWorld()->m_WorldConfig.m_IsDDRace || pChar->m_Weapon == WEAPON_HAMMER) ? -1 : pChar->m_AmmoCount;
|
||||
if(pChar->m_Weapon != m_Core.m_ActiveWeapon)
|
||||
SetActiveWeapon(pChar->m_Weapon);
|
||||
}
|
||||
|
@ -1302,6 +1330,22 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
|
|||
m_ReloadTimer = maximum(0, m_AttackTick + FireDelayTicks - GameWorld()->GameTick());
|
||||
}
|
||||
}
|
||||
|
||||
if(pExtendedDisplayInfo)
|
||||
{
|
||||
if(GameWorld()->m_WorldConfig.m_PredictFreeze)
|
||||
{
|
||||
m_Core.m_FreezeTick = pExtendedDisplayInfo->m_FreezeTick;
|
||||
}
|
||||
m_Core.m_IsInFreeze = pExtendedDisplayInfo->m_IsInFreeze;
|
||||
m_Core.m_Ninja.m_ActivationTick = pExtendedDisplayInfo->m_NinjaActivationTick;
|
||||
m_Core.m_JumpedTotal = pExtendedDisplayInfo->m_JumpedTotal;
|
||||
if(!IsLocal)
|
||||
{
|
||||
m_Input.m_TargetX = pExtendedDisplayInfo->m_TargetX;
|
||||
m_Input.m_TargetY = pExtendedDisplayInfo->m_TargetY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCharacter::SetCoreWorld(CGameWorld *pGameWorld)
|
||||
|
|
|
@ -79,7 +79,6 @@ public:
|
|||
bool m_Jetpack;
|
||||
bool m_NinjaJetpack;
|
||||
int m_FreezeTime;
|
||||
int m_FreezeTick;
|
||||
bool m_FrozenLastTick;
|
||||
bool m_DeepFreeze;
|
||||
bool m_LiveFreeze;
|
||||
|
@ -112,13 +111,13 @@ public:
|
|||
CCharacterCore GetCore() { return m_Core; }
|
||||
void SetCore(CCharacterCore Core) { m_Core = Core; }
|
||||
CCharacterCore *Core() { return &m_Core; }
|
||||
bool GetWeaponGot(int Type) { return m_aWeapons[Type].m_Got; }
|
||||
void SetWeaponGot(int Type, bool Value) { m_aWeapons[Type].m_Got = Value; }
|
||||
int GetWeaponAmmo(int Type) { return m_aWeapons[Type].m_Ammo; }
|
||||
void SetWeaponAmmo(int Type, int Value) { m_aWeapons[Type].m_Ammo = Value; }
|
||||
void SetNinjaActivationDir(vec2 ActivationDir) { m_Ninja.m_ActivationDir = ActivationDir; }
|
||||
void SetNinjaActivationTick(int ActivationTick) { m_Ninja.m_ActivationTick = ActivationTick; }
|
||||
void SetNinjaCurrentMoveTime(int CurrentMoveTime) { m_Ninja.m_CurrentMoveTime = CurrentMoveTime; }
|
||||
bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; }
|
||||
void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; }
|
||||
int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; }
|
||||
void SetWeaponAmmo(int Type, int Value) { m_Core.m_aWeapons[Type].m_Ammo = Value; }
|
||||
void SetNinjaActivationDir(vec2 ActivationDir) { m_Core.m_Ninja.m_ActivationDir = ActivationDir; }
|
||||
void SetNinjaActivationTick(int ActivationTick) { m_Core.m_Ninja.m_ActivationTick = ActivationTick; }
|
||||
void SetNinjaCurrentMoveTime(int CurrentMoveTime) { m_Core.m_Ninja.m_CurrentMoveTime = CurrentMoveTime; }
|
||||
int GetCID() { return m_ID; }
|
||||
void SetInput(CNetObj_PlayerInput *pNewInput)
|
||||
{
|
||||
|
@ -133,8 +132,8 @@ public:
|
|||
int GetAttackTick() { return m_AttackTick; }
|
||||
int GetStrongWeakID() { return m_StrongWeakID; }
|
||||
|
||||
CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended = 0);
|
||||
void Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, bool IsLocal);
|
||||
CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended = 0, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo = 0);
|
||||
void Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo, bool IsLocal);
|
||||
void SetCoreWorld(CGameWorld *pGameWorld);
|
||||
|
||||
int m_LastSnapWeapon;
|
||||
|
@ -153,14 +152,6 @@ private:
|
|||
int m_aHitObjects[10];
|
||||
int m_NumObjectsHit;
|
||||
|
||||
struct WeaponStat
|
||||
{
|
||||
int m_AmmoRegenStart;
|
||||
int m_Ammo;
|
||||
int m_Ammocost;
|
||||
bool m_Got;
|
||||
} m_aWeapons[NUM_WEAPONS];
|
||||
|
||||
int m_LastWeapon;
|
||||
int m_QueuedWeapon;
|
||||
|
||||
|
@ -178,15 +169,6 @@ private:
|
|||
|
||||
int m_NumInputs;
|
||||
|
||||
// ninja
|
||||
struct NinjaStat
|
||||
{
|
||||
vec2 m_ActivationDir;
|
||||
int m_ActivationTick;
|
||||
int m_CurrentMoveTime;
|
||||
int m_OldVelAmount;
|
||||
} m_Ninja;
|
||||
|
||||
// the player core for the physics
|
||||
CCharacterCore m_Core;
|
||||
|
||||
|
|
|
@ -346,16 +346,16 @@ void CGameWorld::NetObjBegin()
|
|||
OnModified();
|
||||
}
|
||||
|
||||
void CGameWorld::NetCharAdd(int ObjID, CNetObj_Character *pCharObj, CNetObj_DDNetCharacter *pExtended, int GameTeam, bool IsLocal)
|
||||
void CGameWorld::NetCharAdd(int ObjID, CNetObj_Character *pCharObj, CNetObj_DDNetCharacter *pExtended, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo, int GameTeam, bool IsLocal)
|
||||
{
|
||||
CCharacter *pChar;
|
||||
if((pChar = (CCharacter *)GetEntity(ObjID, ENTTYPE_CHARACTER)))
|
||||
{
|
||||
pChar->Read(pCharObj, pExtended, IsLocal);
|
||||
pChar->Read(pCharObj, pExtended, pExtendedDisplayInfo, IsLocal);
|
||||
pChar->Keep();
|
||||
}
|
||||
else
|
||||
pChar = new CCharacter(this, ObjID, pCharObj, pExtended);
|
||||
pChar = new CCharacter(this, ObjID, pCharObj, pExtended, pExtendedDisplayInfo);
|
||||
|
||||
if(pChar)
|
||||
pChar->m_GameTeam = GameTeam;
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||
void OnModified();
|
||||
void NetObjBegin();
|
||||
void NetCharAdd(int ObjID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, int GameTeam, bool IsLocal);
|
||||
void NetCharAdd(int ObjID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, CNetObj_DDNetCharacterDisplayInfo *pExtendedDisplayInfo, int GameTeam, bool IsLocal);
|
||||
void NetObjAdd(int ObjID, int ObjType, const void *pObjData, const CNetObj_EntityEx *pDataEx);
|
||||
void NetObjEnd(int LocalID);
|
||||
void CopyWorld(CGameWorld *pFrom);
|
||||
|
|
|
@ -102,7 +102,9 @@ void CCharacterCore::Reset()
|
|||
m_HasTelegunGun = false;
|
||||
m_HasTelegunGrenade = false;
|
||||
m_HasTelegunLaser = false;
|
||||
m_FreezeTick = 0;
|
||||
m_FreezeEnd = 0;
|
||||
m_IsInFreeze = false;
|
||||
m_DeepFrozen = false;
|
||||
m_LiveFrozen = false;
|
||||
|
||||
|
@ -132,35 +134,51 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
float Accel = Grounded ? m_Tuning.m_GroundControlAccel : m_Tuning.m_AirControlAccel;
|
||||
float Friction = Grounded ? m_Tuning.m_GroundFriction : m_Tuning.m_AirFriction;
|
||||
|
||||
// handle jumping
|
||||
// 1 bit = to keep track if a jump has been made on this input (player is holding space bar)
|
||||
// 2 bit = to track if all air-jumps have been used up (tee gets dark feet)
|
||||
if(Grounded)
|
||||
{
|
||||
m_Jumped &= ~2;
|
||||
m_JumpedTotal = 0;
|
||||
}
|
||||
|
||||
// handle input
|
||||
if(UseInput)
|
||||
{
|
||||
m_Direction = m_Input.m_Direction;
|
||||
|
||||
// setup angle
|
||||
float tmp_angle = atan2f(m_Input.m_TargetY, m_Input.m_TargetX);
|
||||
if(tmp_angle < -(pi / 2.0f))
|
||||
float TmpAngle = atan2f(m_Input.m_TargetY, m_Input.m_TargetX);
|
||||
if(TmpAngle < -(pi / 2.0f))
|
||||
{
|
||||
m_Angle = (int)((tmp_angle + (2.0f * pi)) * 256.0f);
|
||||
m_Angle = (int)((TmpAngle + (2.0f * pi)) * 256.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Angle = (int)(tmp_angle * 256.0f);
|
||||
m_Angle = (int)(TmpAngle * 256.0f);
|
||||
}
|
||||
|
||||
// handle jump
|
||||
if(m_Input.m_Jump)
|
||||
{
|
||||
if(!(m_Jumped & 1))
|
||||
if(!(m_Jumped & 1) && m_Jumps != 0)
|
||||
{
|
||||
if(Grounded)
|
||||
{
|
||||
m_TriggeredEvents |= COREEVENT_GROUND_JUMP;
|
||||
m_Vel.y = -m_Tuning.m_GroundJumpImpulse;
|
||||
if(m_Jumps > 1)
|
||||
{
|
||||
m_Jumped |= 1;
|
||||
m_JumpedTotal = 1;
|
||||
}
|
||||
else if(!(m_Jumped & 2))
|
||||
else
|
||||
{
|
||||
m_Jumped |= 3;
|
||||
}
|
||||
m_JumpedTotal = 0;
|
||||
}
|
||||
else if(!(m_Jumped & 2) && m_Jumps >= 1)
|
||||
{
|
||||
m_TriggeredEvents |= COREEVENT_AIR_JUMP;
|
||||
m_Vel.y = -m_Tuning.m_AirJumpImpulse;
|
||||
|
@ -170,7 +188,9 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Jumped &= ~1;
|
||||
}
|
||||
|
||||
// handle hook
|
||||
if(m_Input.m_Hook)
|
||||
|
@ -201,15 +221,6 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
if(m_Direction == 0)
|
||||
m_Vel.x *= Friction;
|
||||
|
||||
// handle jumping
|
||||
// 1 bit = to keep track if a jump has been made on this input (player is holding space bar)
|
||||
// 2 bit = to keep track if a air-jump has been made (tee gets dark feet)
|
||||
if(Grounded)
|
||||
{
|
||||
m_Jumped &= ~2;
|
||||
m_JumpedTotal = 0;
|
||||
}
|
||||
|
||||
// do hook
|
||||
if(m_HookState == HOOK_IDLE)
|
||||
{
|
||||
|
@ -520,7 +531,7 @@ void CCharacterCore::Write(CNetObj_CharacterCore *pObjCore)
|
|||
pObjCore->m_Angle = m_Angle;
|
||||
}
|
||||
|
||||
void CCharacterCore::Read(const CNetObj_CharacterCore *pObjCore)
|
||||
void CCharacterCore::ReadCharacterCore(const CNetObj_CharacterCore *pObjCore)
|
||||
{
|
||||
m_Pos.x = pObjCore->m_X;
|
||||
m_Pos.y = pObjCore->m_Y;
|
||||
|
@ -538,6 +549,11 @@ void CCharacterCore::Read(const CNetObj_CharacterCore *pObjCore)
|
|||
m_Angle = pObjCore->m_Angle;
|
||||
}
|
||||
|
||||
void CCharacterCore::ReadCharacter(const CNetObj_Character *pObjChar)
|
||||
{
|
||||
m_ActiveWeapon = pObjChar->m_Weapon;
|
||||
ReadCharacterCore((const CNetObj_CharacterCore *)pObjChar);
|
||||
}
|
||||
void CCharacterCore::ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet)
|
||||
{
|
||||
// Collision
|
||||
|
@ -568,14 +584,31 @@ void CCharacterCore::ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet)
|
|||
m_HasTelegunGun = pObjDDNet->m_Flags & CHARACTERFLAG_TELEGUN_GUN;
|
||||
m_HasTelegunLaser = pObjDDNet->m_Flags & CHARACTERFLAG_TELEGUN_LASER;
|
||||
|
||||
// Weapons
|
||||
m_aWeapons[WEAPON_HAMMER].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_HAMMER) != 0;
|
||||
m_aWeapons[WEAPON_GUN].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_GUN) != 0;
|
||||
m_aWeapons[WEAPON_SHOTGUN].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_SHOTGUN) != 0;
|
||||
m_aWeapons[WEAPON_GRENADE].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_GRENADE) != 0;
|
||||
m_aWeapons[WEAPON_LASER].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_LASER) != 0;
|
||||
m_aWeapons[WEAPON_NINJA].m_Got = (pObjDDNet->m_Flags & CHARACTERFLAG_WEAPON_NINJA) != 0;
|
||||
|
||||
// Available jumps
|
||||
m_Jumps = pObjDDNet->m_Jumps;
|
||||
}
|
||||
|
||||
void CCharacterCore::ReadDDNetDisplayInfo(const CNetObj_DDNetCharacterDisplayInfo *pObjDDNet)
|
||||
{
|
||||
m_JumpedTotal = pObjDDNet->m_JumpedTotal;
|
||||
m_Ninja.m_ActivationTick = pObjDDNet->m_NinjaActivationTick;
|
||||
m_FreezeTick = pObjDDNet->m_FreezeTick;
|
||||
m_IsInFreeze = pObjDDNet->m_IsInFreeze;
|
||||
}
|
||||
|
||||
void CCharacterCore::Quantize()
|
||||
{
|
||||
CNetObj_CharacterCore Core;
|
||||
Write(&Core);
|
||||
Read(&Core);
|
||||
ReadCharacterCore(&Core);
|
||||
}
|
||||
|
||||
// DDRace
|
||||
|
|
|
@ -221,11 +221,29 @@ public:
|
|||
int m_HookTick;
|
||||
int m_HookState;
|
||||
int m_HookedPlayer;
|
||||
|
||||
int m_ActiveWeapon;
|
||||
struct WeaponStat
|
||||
{
|
||||
int m_AmmoRegenStart;
|
||||
int m_Ammo;
|
||||
int m_Ammocost;
|
||||
bool m_Got;
|
||||
} m_aWeapons[NUM_WEAPONS];
|
||||
|
||||
// ninja
|
||||
struct
|
||||
{
|
||||
vec2 m_ActivationDir;
|
||||
int m_ActivationTick;
|
||||
int m_CurrentMoveTime;
|
||||
int m_OldVelAmount;
|
||||
} m_Ninja;
|
||||
|
||||
bool m_NewHook;
|
||||
|
||||
int m_Jumped;
|
||||
// m_JumpedTotal counts the jumps performed in the air
|
||||
int m_JumpedTotal;
|
||||
int m_Jumps;
|
||||
|
||||
|
@ -240,7 +258,8 @@ public:
|
|||
void Tick(bool UseInput);
|
||||
void Move();
|
||||
|
||||
void Read(const CNetObj_CharacterCore *pObjCore);
|
||||
void ReadCharacterCore(const CNetObj_CharacterCore *pObjCore);
|
||||
void ReadCharacter(const CNetObj_Character *pObjChar);
|
||||
void Write(CNetObj_CharacterCore *pObjCore);
|
||||
void Quantize();
|
||||
|
||||
|
@ -258,6 +277,7 @@ public:
|
|||
void SetTeamsCore(CTeamsCore *pTeams);
|
||||
void SetTeleOuts(std::map<int, std::vector<vec2>> *pTeleOuts);
|
||||
void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet);
|
||||
void ReadDDNetDisplayInfo(const CNetObj_DDNetCharacterDisplayInfo *pObjDDNet);
|
||||
bool m_Solo;
|
||||
bool m_Jetpack;
|
||||
bool m_NoCollision;
|
||||
|
@ -272,7 +292,9 @@ public:
|
|||
bool m_HasTelegunGun;
|
||||
bool m_HasTelegunGrenade;
|
||||
bool m_HasTelegunLaser;
|
||||
int m_FreezeTick;
|
||||
int m_FreezeEnd;
|
||||
bool m_IsInFreeze;
|
||||
bool m_DeepFrozen;
|
||||
bool m_LiveFrozen;
|
||||
CTuningParams m_Tuning;
|
||||
|
|
|
@ -158,14 +158,14 @@ void CCharacter::HandleJetpack()
|
|||
if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses)
|
||||
WillFire = true;
|
||||
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
WillFire = true;
|
||||
|
||||
if(!WillFire)
|
||||
return;
|
||||
|
||||
// check for ammo
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo || m_FreezeTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -192,14 +192,14 @@ void CCharacter::HandleNinja()
|
|||
if(m_Core.m_ActiveWeapon != WEAPON_NINJA)
|
||||
return;
|
||||
|
||||
if((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000))
|
||||
if((Server()->Tick() - m_Core.m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000))
|
||||
{
|
||||
// time's up, return
|
||||
RemoveNinja();
|
||||
return;
|
||||
}
|
||||
|
||||
int NinjaTime = m_Ninja.m_ActivationTick + (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000) - Server()->Tick();
|
||||
int NinjaTime = m_Core.m_Ninja.m_ActivationTick + (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000) - Server()->Tick();
|
||||
|
||||
if(NinjaTime % Server()->TickSpeed() == 0 && NinjaTime / Server()->TickSpeed() <= 5)
|
||||
{
|
||||
|
@ -211,18 +211,18 @@ void CCharacter::HandleNinja()
|
|||
// force ninja Weapon
|
||||
SetWeapon(WEAPON_NINJA);
|
||||
|
||||
m_Ninja.m_CurrentMoveTime--;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime--;
|
||||
|
||||
if(m_Ninja.m_CurrentMoveTime == 0)
|
||||
if(m_Core.m_Ninja.m_CurrentMoveTime == 0)
|
||||
{
|
||||
// reset velocity
|
||||
m_Core.m_Vel = m_Ninja.m_ActivationDir * m_Ninja.m_OldVelAmount;
|
||||
m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * m_Core.m_Ninja.m_OldVelAmount;
|
||||
}
|
||||
|
||||
if(m_Ninja.m_CurrentMoveTime > 0)
|
||||
if(m_Core.m_Ninja.m_CurrentMoveTime > 0)
|
||||
{
|
||||
// Set velocity
|
||||
m_Core.m_Vel = m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity;
|
||||
m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity;
|
||||
vec2 OldPos = m_Pos;
|
||||
GameServer()->Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), 0.f);
|
||||
|
||||
|
@ -285,7 +285,7 @@ void CCharacter::HandleNinja()
|
|||
void CCharacter::DoWeaponSwitch()
|
||||
{
|
||||
// make sure we can switch
|
||||
if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_aWeapons[WEAPON_NINJA].m_Got || !m_aWeapons[m_QueuedWeapon].m_Got)
|
||||
if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_Core.m_aWeapons[WEAPON_NINJA].m_Got || !m_Core.m_aWeapons[m_QueuedWeapon].m_Got)
|
||||
return;
|
||||
|
||||
// switch Weapon
|
||||
|
@ -300,7 +300,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
|
||||
bool Anything = false;
|
||||
for(int i = 0; i < NUM_WEAPONS - 1; ++i)
|
||||
if(m_aWeapons[i].m_Got)
|
||||
if(m_Core.m_aWeapons[i].m_Got)
|
||||
Anything = true;
|
||||
if(!Anything)
|
||||
return;
|
||||
|
@ -313,7 +313,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
while(Next) // Next Weapon selection
|
||||
{
|
||||
WantedWeapon = (WantedWeapon + 1) % NUM_WEAPONS;
|
||||
if(m_aWeapons[WantedWeapon].m_Got)
|
||||
if(m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
Next--;
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
while(Prev) // Prev Weapon selection
|
||||
{
|
||||
WantedWeapon = (WantedWeapon - 1) < 0 ? NUM_WEAPONS - 1 : WantedWeapon - 1;
|
||||
if(m_aWeapons[WantedWeapon].m_Got)
|
||||
if(m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
Prev--;
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ void CCharacter::HandleWeaponSwitch()
|
|||
WantedWeapon = m_Input.m_WantedWeapon - 1;
|
||||
|
||||
// check for insane values
|
||||
if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_Core.m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got)
|
||||
if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_Core.m_ActiveWeapon && m_Core.m_aWeapons[WantedWeapon].m_Got)
|
||||
m_QueuedWeapon = WantedWeapon;
|
||||
|
||||
DoWeaponSwitch();
|
||||
|
@ -372,7 +372,7 @@ void CCharacter::FireWeapon()
|
|||
if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses)
|
||||
WillFire = true;
|
||||
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
if(FullAuto && (m_LatestInput.m_Fire & 1) && m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
WillFire = true;
|
||||
|
||||
if(!WillFire)
|
||||
|
@ -390,7 +390,7 @@ void CCharacter::FireWeapon()
|
|||
}
|
||||
|
||||
// check for ammo
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo)
|
||||
{
|
||||
/*// 125ms is a magical limit of how fast a human can click
|
||||
m_ReloadTimer = 125 * Server()->TickSpeed() / 1000;
|
||||
|
@ -576,9 +576,9 @@ void CCharacter::FireWeapon()
|
|||
// reset Hit objects
|
||||
m_NumObjectsHit = 0;
|
||||
|
||||
m_Ninja.m_ActivationDir = Direction;
|
||||
m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
|
||||
m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
m_Core.m_Ninja.m_ActivationDir = Direction;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
|
||||
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
}
|
||||
|
@ -587,8 +587,8 @@ void CCharacter::FireWeapon()
|
|||
|
||||
m_AttackTick = Server()->Tick();
|
||||
|
||||
/*if(m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited
|
||||
m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo--;*/
|
||||
/*if(m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited
|
||||
m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo--;*/
|
||||
|
||||
if(!m_ReloadTimer)
|
||||
{
|
||||
|
@ -623,21 +623,21 @@ void CCharacter::HandleWeapons()
|
|||
|
||||
void CCharacter::GiveNinja()
|
||||
{
|
||||
m_Ninja.m_ActivationTick = Server()->Tick();
|
||||
m_aWeapons[WEAPON_NINJA].m_Got = true;
|
||||
m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
|
||||
m_Core.m_Ninja.m_ActivationTick = Server()->Tick();
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Got = true;
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
|
||||
if(m_Core.m_ActiveWeapon != WEAPON_NINJA)
|
||||
m_LastWeapon = m_Core.m_ActiveWeapon;
|
||||
m_Core.m_ActiveWeapon = WEAPON_NINJA;
|
||||
|
||||
if(!m_aWeapons[WEAPON_NINJA].m_Got)
|
||||
if(!m_Core.m_aWeapons[WEAPON_NINJA].m_Got)
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
}
|
||||
|
||||
void CCharacter::RemoveNinja()
|
||||
{
|
||||
m_Ninja.m_CurrentMoveTime = 0;
|
||||
m_aWeapons[WEAPON_NINJA].m_Got = false;
|
||||
m_Core.m_Ninja.m_CurrentMoveTime = 0;
|
||||
m_Core.m_aWeapons[WEAPON_NINJA].m_Got = false;
|
||||
m_Core.m_ActiveWeapon = m_LastWeapon;
|
||||
|
||||
SetWeapon(m_Core.m_ActiveWeapon);
|
||||
|
@ -863,12 +863,12 @@ void CCharacter::TickPaused()
|
|||
{
|
||||
++m_AttackTick;
|
||||
++m_DamageTakenTick;
|
||||
++m_Ninja.m_ActivationTick;
|
||||
++m_Core.m_Ninja.m_ActivationTick;
|
||||
++m_ReckoningTick;
|
||||
if(m_LastAction != -1)
|
||||
++m_LastAction;
|
||||
if(m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart > -1)
|
||||
++m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart;
|
||||
if(m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart > -1)
|
||||
++m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart;
|
||||
if(m_EmoteStop > -1)
|
||||
++m_EmoteStop;
|
||||
}
|
||||
|
@ -1089,8 +1089,8 @@ void CCharacter::SnapCharacter(int SnappingClient, int ID)
|
|||
{
|
||||
Health = m_Health;
|
||||
Armor = m_Armor;
|
||||
if(m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0)
|
||||
AmmoCount = (!m_FreezeTime) ? m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo : 0;
|
||||
if(m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0)
|
||||
AmmoCount = (!m_FreezeTime) ? m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo : 0;
|
||||
}
|
||||
|
||||
if(GetPlayer()->m_Afk || GetPlayer()->IsPaused())
|
||||
|
@ -1152,9 +1152,9 @@ void CCharacter::SnapCharacter(int SnappingClient, int ID)
|
|||
pCharacter->m_AmmoCount = AmmoCount;
|
||||
|
||||
if(m_FreezeTime > 0 || m_FreezeTime == -1 || m_DeepFreeze)
|
||||
pCharacter->m_AmmoCount = m_FreezeTick + g_Config.m_SvFreezeDelay * Server()->TickSpeed();
|
||||
pCharacter->m_AmmoCount = m_Core.m_FreezeTick + g_Config.m_SvFreezeDelay * Server()->TickSpeed();
|
||||
else if(Weapon == WEAPON_NINJA)
|
||||
pCharacter->m_AmmoCount = m_Ninja.m_ActivationTick + g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000;
|
||||
pCharacter->m_AmmoCount = m_Core.m_Ninja.m_ActivationTick + g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000;
|
||||
|
||||
pCharacter->m_Health = Health;
|
||||
pCharacter->m_Armor = Armor;
|
||||
|
@ -1228,15 +1228,15 @@ void CCharacter::Snap(int SnappingClient)
|
|||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_TELEGUN_GRENADE;
|
||||
if(m_Core.m_HasTelegunLaser)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_TELEGUN_LASER;
|
||||
if(m_aWeapons[WEAPON_HAMMER].m_Got)
|
||||
if(m_Core.m_aWeapons[WEAPON_HAMMER].m_Got)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_HAMMER;
|
||||
if(m_aWeapons[WEAPON_GUN].m_Got)
|
||||
if(m_Core.m_aWeapons[WEAPON_GUN].m_Got)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_GUN;
|
||||
if(m_aWeapons[WEAPON_SHOTGUN].m_Got)
|
||||
if(m_Core.m_aWeapons[WEAPON_SHOTGUN].m_Got)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_SHOTGUN;
|
||||
if(m_aWeapons[WEAPON_GRENADE].m_Got)
|
||||
if(m_Core.m_aWeapons[WEAPON_GRENADE].m_Got)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_GRENADE;
|
||||
if(m_aWeapons[WEAPON_LASER].m_Got)
|
||||
if(m_Core.m_aWeapons[WEAPON_LASER].m_Got)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_LASER;
|
||||
if(m_Core.m_ActiveWeapon == WEAPON_NINJA)
|
||||
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_NINJA;
|
||||
|
@ -1249,6 +1249,18 @@ void CCharacter::Snap(int SnappingClient)
|
|||
pDDNetCharacter->m_Jumps = m_Core.m_Jumps;
|
||||
pDDNetCharacter->m_TeleCheckpoint = m_TeleCheckpoint;
|
||||
pDDNetCharacter->m_StrongWeakID = m_StrongWeakID;
|
||||
|
||||
CNetObj_DDNetCharacterDisplayInfo *pDDNetCharacterDisplayInfo = static_cast<CNetObj_DDNetCharacterDisplayInfo *>(Server()->SnapNewItem(NETOBJTYPE_DDNETCHARACTERDISPLAYINFO, ID, sizeof(CNetObj_DDNetCharacterDisplayInfo)));
|
||||
if(!pDDNetCharacterDisplayInfo)
|
||||
return;
|
||||
pDDNetCharacterDisplayInfo->m_JumpedTotal = m_Core.m_JumpedTotal;
|
||||
pDDNetCharacterDisplayInfo->m_NinjaActivationTick = m_Core.m_Ninja.m_ActivationTick;
|
||||
pDDNetCharacterDisplayInfo->m_FreezeTick = m_Core.m_FreezeTick;
|
||||
pDDNetCharacterDisplayInfo->m_IsInFreeze = m_Core.m_IsInFreeze;
|
||||
pDDNetCharacterDisplayInfo->m_IsInPracticeMode = Teams()->IsPractice(Team());
|
||||
pDDNetCharacterDisplayInfo->m_TargetX = m_Core.m_Input.m_TargetX;
|
||||
pDDNetCharacterDisplayInfo->m_TargetY = m_Core.m_Input.m_TargetY;
|
||||
pDDNetCharacterDisplayInfo->m_RampValue = round_to_int(VelocityRamp(length(m_Core.m_Vel) * 50, m_Core.m_Tuning.m_VelrampStart, m_Core.m_Tuning.m_VelrampRange, m_Core.m_Tuning.m_VelrampCurvature) * 1000.0f);
|
||||
}
|
||||
|
||||
// DDRace
|
||||
|
@ -1483,7 +1495,9 @@ void CCharacter::HandleTiles(int Index)
|
|||
|
||||
// freeze
|
||||
if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Super && !m_DeepFreeze)
|
||||
{
|
||||
Freeze();
|
||||
}
|
||||
else if(((m_TileIndex == TILE_UNFREEZE) || (m_TileFIndex == TILE_UNFREEZE)) && !m_DeepFreeze)
|
||||
UnFreeze();
|
||||
|
||||
|
@ -1603,7 +1617,7 @@ void CCharacter::HandleTiles(int Index)
|
|||
if(m_Core.m_Vel.y > 0 && m_Core.m_Colliding && m_Core.m_LeftWall)
|
||||
{
|
||||
m_Core.m_LeftWall = false;
|
||||
m_Core.m_JumpedTotal = m_Core.m_Jumps - 1;
|
||||
m_Core.m_JumpedTotal = m_Core.m_Jumps >= 2 ? m_Core.m_Jumps - 2 : 0;
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
}
|
||||
|
@ -1704,8 +1718,10 @@ void CCharacter::HandleTiles(int Index)
|
|||
else if(GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER)
|
||||
{
|
||||
if(GameServer()->Collision()->GetSwitchNumber(MapIndex) == 0 || GameServer()->Collision()->m_pSwitchers[GameServer()->Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()])
|
||||
{
|
||||
Freeze(GameServer()->Collision()->GetSwitchDelay(MapIndex));
|
||||
}
|
||||
}
|
||||
else if(GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER)
|
||||
{
|
||||
if(GameServer()->Collision()->GetSwitchNumber(MapIndex) == 0 || GameServer()->Collision()->m_pSwitchers[GameServer()->Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()])
|
||||
|
@ -2080,7 +2096,7 @@ void CCharacter::DDRaceTick()
|
|||
if(m_FreezeTime > 0)
|
||||
m_FreezeTime--;
|
||||
else
|
||||
m_Ninja.m_ActivationTick = Server()->Tick();
|
||||
m_Core.m_Ninja.m_ActivationTick = Server()->Tick();
|
||||
m_Input.m_Direction = 0;
|
||||
m_Input.m_Jump = 0;
|
||||
m_Input.m_Hook = 0;
|
||||
|
@ -2090,31 +2106,30 @@ void CCharacter::DDRaceTick()
|
|||
|
||||
HandleTuneLayer(); // need this before coretick
|
||||
|
||||
// look for save position for rescue feature
|
||||
if(g_Config.m_SvRescue || ((g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || Team() > TEAM_FLOCK) && Team() >= TEAM_FLOCK && Team() < TEAM_SUPER))
|
||||
{
|
||||
// check if the tee is in any type of freeze
|
||||
int Index = GameServer()->Collision()->GetPureMapIndex(m_Pos);
|
||||
const int aTiles[] = {
|
||||
GameServer()->Collision()->GetTileIndex(Index),
|
||||
GameServer()->Collision()->GetFTileIndex(Index),
|
||||
GameServer()->Collision()->GetSwitchType(Index)};
|
||||
if(IsGrounded() && !m_DeepFreeze)
|
||||
{
|
||||
bool IsInFreeze = false;
|
||||
m_Core.m_IsInFreeze = false;
|
||||
for(const int Tile : aTiles)
|
||||
{
|
||||
if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE)
|
||||
{
|
||||
IsInFreeze = true;
|
||||
m_Core.m_IsInFreeze = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!IsInFreeze)
|
||||
|
||||
// look for save position for rescue feature
|
||||
if(g_Config.m_SvRescue || ((g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || Team() > TEAM_FLOCK) && Team() >= TEAM_FLOCK && Team() < TEAM_SUPER))
|
||||
{
|
||||
if(!m_Core.m_IsInFreeze && IsGrounded() && !m_DeepFreeze)
|
||||
{
|
||||
SetRescue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Core.m_Id = GetPlayer()->GetCID();
|
||||
}
|
||||
|
@ -2131,17 +2146,32 @@ void CCharacter::DDRacePostCoreTick()
|
|||
if(m_DeepFreeze && !m_Super)
|
||||
Freeze();
|
||||
|
||||
if(m_Core.m_Jumps == -1 && !m_Super)
|
||||
if(m_Core.m_Jumps == -1)
|
||||
{
|
||||
// The player has only one ground jump, so his feet are always dark
|
||||
m_Core.m_Jumped |= 2;
|
||||
else if(m_Core.m_Jumps == 0 && !m_Super)
|
||||
m_Core.m_Jumped = 3;
|
||||
}
|
||||
else if(m_Core.m_Jumps == 0)
|
||||
{
|
||||
// The player has no jumps at all, so his feet are always dark
|
||||
m_Core.m_Jumped |= 2;
|
||||
}
|
||||
else if(m_Core.m_Jumps == 1 && m_Core.m_Jumped > 0)
|
||||
m_Core.m_Jumped = 3;
|
||||
{
|
||||
// If the player has only one jump, each jump is the last one
|
||||
m_Core.m_Jumped |= 2;
|
||||
}
|
||||
else if(m_Core.m_JumpedTotal < m_Core.m_Jumps - 1 && m_Core.m_Jumped > 1)
|
||||
{
|
||||
// The player has not yet used up all his jumps, so his feet remain light
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
|
||||
if((m_Super || m_SuperJump) && m_Core.m_Jumped > 1)
|
||||
{
|
||||
// Super players and players with infinite jumps always have light feet
|
||||
m_Core.m_Jumped = 1;
|
||||
}
|
||||
|
||||
int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_Pos);
|
||||
HandleSkippableTiles(CurrentIndex);
|
||||
|
@ -2186,11 +2216,11 @@ bool CCharacter::Freeze(int Seconds)
|
|||
{
|
||||
if((Seconds <= 0 || m_Super || m_FreezeTime == -1 || m_FreezeTime > Seconds * Server()->TickSpeed()) && Seconds != -1)
|
||||
return false;
|
||||
if(m_FreezeTick < Server()->Tick() - Server()->TickSpeed() || Seconds == -1)
|
||||
if(m_Core.m_FreezeTick < Server()->Tick() - Server()->TickSpeed() || Seconds == -1)
|
||||
{
|
||||
m_Armor = 0;
|
||||
m_FreezeTime = Seconds == -1 ? Seconds : Seconds * Server()->TickSpeed();
|
||||
m_FreezeTick = Server()->Tick();
|
||||
m_Core.m_FreezeTick = Server()->Tick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -2206,10 +2236,10 @@ bool CCharacter::UnFreeze()
|
|||
if(m_FreezeTime > 0)
|
||||
{
|
||||
m_Armor = 10;
|
||||
if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Got)
|
||||
if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Got)
|
||||
m_Core.m_ActiveWeapon = WEAPON_GUN;
|
||||
m_FreezeTime = 0;
|
||||
m_FreezeTick = 0;
|
||||
m_Core.m_FreezeTick = 0;
|
||||
m_FrozenLastTick = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -2234,10 +2264,10 @@ void CCharacter::GiveWeapon(int Weapon, bool Remove)
|
|||
}
|
||||
else
|
||||
{
|
||||
m_aWeapons[Weapon].m_Ammo = -1;
|
||||
m_Core.m_aWeapons[Weapon].m_Ammo = -1;
|
||||
}
|
||||
|
||||
m_aWeapons[Weapon].m_Got = !Remove;
|
||||
m_Core.m_aWeapons[Weapon].m_Got = !Remove;
|
||||
}
|
||||
|
||||
void CCharacter::GiveAllWeapons()
|
||||
|
@ -2252,7 +2282,7 @@ void CCharacter::ResetPickups()
|
|||
{
|
||||
for(int i = WEAPON_SHOTGUN; i < NUM_WEAPONS - 1; i++)
|
||||
{
|
||||
m_aWeapons[i].m_Got = false;
|
||||
m_Core.m_aWeapons[i].m_Got = false;
|
||||
if(m_Core.m_ActiveWeapon == i)
|
||||
m_Core.m_ActiveWeapon = WEAPON_GUN;
|
||||
}
|
||||
|
|
|
@ -98,15 +98,6 @@ private:
|
|||
CEntity *m_apHitObjects[10];
|
||||
int m_NumObjectsHit;
|
||||
|
||||
struct WeaponStat
|
||||
{
|
||||
int m_AmmoRegenStart;
|
||||
int m_Ammo;
|
||||
int m_Ammocost;
|
||||
bool m_Got;
|
||||
|
||||
} m_aWeapons[NUM_WEAPONS];
|
||||
|
||||
int m_LastWeapon;
|
||||
int m_QueuedWeapon;
|
||||
|
||||
|
@ -139,15 +130,6 @@ private:
|
|||
int m_Health;
|
||||
int m_Armor;
|
||||
|
||||
// ninja
|
||||
struct
|
||||
{
|
||||
vec2 m_ActivationDir;
|
||||
int m_ActivationTick;
|
||||
int m_CurrentMoveTime;
|
||||
int m_OldVelAmount;
|
||||
} m_Ninja;
|
||||
|
||||
// the player core for the physics
|
||||
CCharacterCore m_Core;
|
||||
CGameTeams *m_pTeams = nullptr;
|
||||
|
@ -203,7 +185,6 @@ public:
|
|||
bool m_NinjaJetpack;
|
||||
int m_TeamBeforeSuper;
|
||||
int m_FreezeTime;
|
||||
int m_FreezeTick;
|
||||
bool m_FrozenLastTick;
|
||||
bool m_DeepFreeze;
|
||||
bool m_LiveFreeze;
|
||||
|
@ -259,14 +240,14 @@ public:
|
|||
CCharacterCore GetCore() { return m_Core; }
|
||||
void SetCore(CCharacterCore Core) { m_Core = Core; }
|
||||
CCharacterCore *Core() { return &m_Core; }
|
||||
bool GetWeaponGot(int Type) { return m_aWeapons[Type].m_Got; }
|
||||
void SetWeaponGot(int Type, bool Value) { m_aWeapons[Type].m_Got = Value; }
|
||||
int GetWeaponAmmo(int Type) { return m_aWeapons[Type].m_Ammo; }
|
||||
void SetWeaponAmmo(int Type, int Value) { m_aWeapons[Type].m_Ammo = Value; }
|
||||
bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; }
|
||||
void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; }
|
||||
int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; }
|
||||
void SetWeaponAmmo(int Type, int Value) { m_Core.m_aWeapons[Type].m_Ammo = Value; }
|
||||
bool IsAlive() { return m_Alive; }
|
||||
void SetNinjaActivationDir(vec2 ActivationDir) { m_Ninja.m_ActivationDir = ActivationDir; }
|
||||
void SetNinjaActivationTick(int ActivationTick) { m_Ninja.m_ActivationTick = ActivationTick; }
|
||||
void SetNinjaCurrentMoveTime(int CurrentMoveTime) { m_Ninja.m_CurrentMoveTime = CurrentMoveTime; }
|
||||
void SetNinjaActivationDir(vec2 ActivationDir) { m_Core.m_Ninja.m_ActivationDir = ActivationDir; }
|
||||
void SetNinjaActivationTick(int ActivationTick) { m_Core.m_Ninja.m_ActivationTick = ActivationTick; }
|
||||
void SetNinjaCurrentMoveTime(int CurrentMoveTime) { m_Core.m_Ninja.m_CurrentMoveTime = CurrentMoveTime; }
|
||||
|
||||
int GetLastAction() const { return m_LastAction; }
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ void CSaveTee::Save(CCharacter *pChr)
|
|||
|
||||
for(int i = 0; i < NUM_WEAPONS; i++)
|
||||
{
|
||||
m_aWeapons[i].m_AmmoRegenStart = pChr->m_aWeapons[i].m_AmmoRegenStart;
|
||||
m_aWeapons[i].m_Ammo = pChr->m_aWeapons[i].m_Ammo;
|
||||
m_aWeapons[i].m_Ammocost = pChr->m_aWeapons[i].m_Ammocost;
|
||||
m_aWeapons[i].m_Got = pChr->m_aWeapons[i].m_Got;
|
||||
m_aWeapons[i].m_AmmoRegenStart = pChr->m_Core.m_aWeapons[i].m_AmmoRegenStart;
|
||||
m_aWeapons[i].m_Ammo = pChr->m_Core.m_aWeapons[i].m_Ammo;
|
||||
m_aWeapons[i].m_Ammocost = pChr->m_Core.m_aWeapons[i].m_Ammocost;
|
||||
m_aWeapons[i].m_Got = pChr->m_Core.m_aWeapons[i].m_Got;
|
||||
}
|
||||
|
||||
m_LastWeapon = pChr->m_LastWeapon;
|
||||
|
@ -39,7 +39,7 @@ void CSaveTee::Save(CCharacter *pChr)
|
|||
m_Jetpack = pChr->m_Jetpack;
|
||||
m_NinjaJetpack = pChr->m_NinjaJetpack;
|
||||
m_FreezeTime = pChr->m_FreezeTime;
|
||||
m_FreezeTick = pChr->Server()->Tick() - pChr->m_FreezeTick;
|
||||
m_FreezeTick = pChr->Server()->Tick() - pChr->m_Core.m_FreezeTick;
|
||||
|
||||
m_DeepFreeze = pChr->m_DeepFreeze;
|
||||
m_LiveFreeze = pChr->m_LiveFreeze;
|
||||
|
@ -120,11 +120,11 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap)
|
|||
|
||||
for(int i = 0; i < NUM_WEAPONS; i++)
|
||||
{
|
||||
pChr->m_aWeapons[i].m_AmmoRegenStart = m_aWeapons[i].m_AmmoRegenStart;
|
||||
pChr->m_Core.m_aWeapons[i].m_AmmoRegenStart = m_aWeapons[i].m_AmmoRegenStart;
|
||||
// m_Ammo not used anymore for tracking freeze following https://github.com/ddnet/ddnet/pull/2086
|
||||
pChr->m_aWeapons[i].m_Ammo = -1;
|
||||
pChr->m_aWeapons[i].m_Ammocost = m_aWeapons[i].m_Ammocost;
|
||||
pChr->m_aWeapons[i].m_Got = m_aWeapons[i].m_Got;
|
||||
pChr->m_Core.m_aWeapons[i].m_Ammo = -1;
|
||||
pChr->m_Core.m_aWeapons[i].m_Ammocost = m_aWeapons[i].m_Ammocost;
|
||||
pChr->m_Core.m_aWeapons[i].m_Got = m_aWeapons[i].m_Got;
|
||||
}
|
||||
|
||||
pChr->m_LastWeapon = m_LastWeapon;
|
||||
|
@ -134,7 +134,7 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap)
|
|||
pChr->m_Jetpack = m_Jetpack;
|
||||
pChr->m_NinjaJetpack = m_NinjaJetpack;
|
||||
pChr->m_FreezeTime = m_FreezeTime;
|
||||
pChr->m_FreezeTick = pChr->Server()->Tick() - m_FreezeTick;
|
||||
pChr->m_Core.m_FreezeTick = pChr->Server()->Tick() - m_FreezeTick;
|
||||
|
||||
pChr->m_DeepFreeze = m_DeepFreeze;
|
||||
pChr->m_LiveFreeze = m_LiveFreeze;
|
||||
|
|
|
@ -43,6 +43,10 @@ MACRO_CONFIG_INT(ClShowhud, cl_showhud, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE,
|
|||
MACRO_CONFIG_INT(ClShowhudHealthAmmo, cl_showhud_healthammo, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Health + Ammo)")
|
||||
MACRO_CONFIG_INT(ClShowhudScore, cl_showhud_score, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Score)")
|
||||
MACRO_CONFIG_INT(ClShowhudTimer, cl_showhud_timer, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Timer)")
|
||||
MACRO_CONFIG_INT(ClShowhudDummyActions, cl_showhud_dummy_actions, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Dummy Actions)")
|
||||
MACRO_CONFIG_INT(ClShowhudPlayerPosition, cl_showhud_player_position, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Player Position)")
|
||||
MACRO_CONFIG_INT(ClShowhudPlayerSpeed, cl_showhud_player_speed, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Player Speed)")
|
||||
MACRO_CONFIG_INT(ClShowhudPlayerAngle, cl_showhud_player_angle, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame HUD (Player Aim Angle)")
|
||||
MACRO_CONFIG_INT(ClShowRecord, cl_showrecord, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show old style DDRace client records")
|
||||
MACRO_CONFIG_INT(ClShowNotifications, cl_shownotifications, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Make the client notify when someone highlights you")
|
||||
MACRO_CONFIG_INT(ClShowEmotes, cl_showemotes, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show tee emotes")
|
||||
|
@ -201,5 +205,6 @@ MACRO_CONFIG_INT(DbgDummies, dbg_dummies, 0, 0, 15, CFGFLAG_SERVER, "(Debug buil
|
|||
#endif
|
||||
|
||||
MACRO_CONFIG_INT(DbgFocus, dbg_focus, 0, 0, 1, CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(DbgTuning, dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(DbgTuning, dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "Display information about the tuning parameters that affect the own player")
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue