5639: Add different laser colors for different types r=def- a=VoxelDoesCode

Since we're doing a push to assist newer players, I think it would be a good idea to differentiate the different types of lasers, so that non-moving laser entities aren't confused with doors (I'm aware the blinking is a sign, but it's very subtle). This also includes a new color for Shotgun's laser, which would be a nice touch for both customization and clarity. 

![image](https://user-images.githubusercontent.com/95713843/179640279-06bb52a0-9070-48ca-b39f-013034f9c16e.png)
![image](https://user-images.githubusercontent.com/95713843/179640326-bcfad3a9-6209-4514-8850-98c5146e28b8.png)

If this were to be implemented though, it would require a server update, because the server actually needs to send the DDNetLaser to clients with a high enough client version (It's set to 17000 currently). As of right now it's a solid concept.

Huge thanks to Fokkonaut for finalizing this concept!

## 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 (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [x] Changed no physics that affect existing maps
- [ ] 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: heinrich5991 <heinrich5991@gmail.com>
Co-authored-by: VoxelDoesCode <bluheadcat@gmail.com>
Co-authored-by: fokkonaut <35420825+fokkonaut@users.noreply.github.com>
This commit is contained in:
bors[bot] 2022-09-15 07:14:11 +00:00 committed by GitHub
commit ed2b0f40b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 309 additions and 58 deletions

View file

@ -2073,6 +2073,8 @@ if(CLIENT)
components/voting.h
gameclient.cpp
gameclient.h
laser_data.cpp
laser_data.h
lineinput.cpp
lineinput.h
prediction/entities/character.cpp

View file

@ -34,6 +34,8 @@ ProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [
"EXPLOSIVE", "FREEZE",
]
LaserTypes = ["RIFLE", "SHOTGUN", "DOOR", "FREEZE"]
Emoticons = ["OOP", "EXCLAMATION", "HEARTS", "DROP", "DOTDOT", "MUSIC", "SORRY", "GHOST", "SUSHI", "SPLATTEE", "DEVILTEE", "ZOMG", "ZZZ", "WTF", "EYES", "QUESTION"]
Powerups = ["HEALTH", "ARMOR", "WEAPON", "NINJA", "ARMOR_SHOTGUN", "ARMOR_GRENADE", "ARMOR_NINJA", "ARMOR_LASER"]
@ -78,6 +80,7 @@ Enums = [
Enum("EMOTICON", Emoticons),
Enum("AUTHED", Authed),
Enum("ENTITYCLASS", EntityClasses),
Enum("LASERTYPE", LaserTypes),
]
Flags = [
@ -273,6 +276,16 @@ Objects = [
NetTick("m_StartTick"),
]),
NetObjectEx("DDNetLaser", "laser@netobj.ddnet.tw", [
NetIntAny("m_ToX"),
NetIntAny("m_ToY"),
NetIntAny("m_FromX"),
NetIntAny("m_FromY"),
NetTick("m_StartTick"),
NetIntRange("m_Owner", 0, 'MAX_CLIENTS-1'),
NetIntAny("m_Type"),
]),
## Events
NetEvent("Common", [

View file

@ -296,8 +296,14 @@ MACRO_CONFIG_COL(ClMessageClientColor, cl_message_client_color, 9633471, CFGFLAG
MACRO_CONFIG_COL(ClMessageHighlightColor, cl_message_highlight_color, 65471, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlighted message color")
MACRO_CONFIG_COL(ClMessageTeamColor, cl_message_team_color, 5636050, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Team message color")
MACRO_CONFIG_COL(ClMessageColor, cl_message_color, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Message color")
MACRO_CONFIG_COL(ClLaserInnerColor, cl_laser_inner_color, 11206591, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color")
MACRO_CONFIG_COL(ClLaserOutlineColor, cl_laser_outline_color, 11176233, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color")
MACRO_CONFIG_COL(ClLaserRifleInnerColor, cl_laser_rifle_inner_color, 11206591, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color for Rifle")
MACRO_CONFIG_COL(ClLaserRifleOutlineColor, cl_laser_rifle_outline_color, 11176233, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color for Rifle")
MACRO_CONFIG_COL(ClLaserShotgunInnerColor, cl_laser_sg_inner_color, 1900385, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color for Shotgun")
MACRO_CONFIG_COL(ClLaserShotgunOutlineColor, cl_laser_sg_outline_color, 1866773, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color for Shotgun")
MACRO_CONFIG_COL(ClLaserDoorInnerColor, cl_laser_door_inner_color, 7701379, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color for doors")
MACRO_CONFIG_COL(ClLaserDoorOutlineColor, cl_laser_door_outline_color, 7667473, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color for doors")
MACRO_CONFIG_COL(ClLaserFreezeInnerColor, cl_laser_freeze_inner_color, 15958915, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color for freezes")
MACRO_CONFIG_COL(ClLaserFreezeOutlineColor, cl_laser_freeze_outline_color, 15972381, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color for freezes")
MACRO_CONFIG_COL(ClKillMessageNormalColor, cl_kill_message_normal_color, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_COLALPHA, "Kill message normal color")
MACRO_CONFIG_COL(ClKillMessageHighlightColor, cl_kill_message_highlight_color, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_COLALPHA, "Kill message highlight color")

View file

@ -120,6 +120,7 @@ enum
VERSION_DDNET_INDEPENDENT_SPECTATORS_TEAM = 16000,
VERSION_DDNET_WEAPON_SHIELDS = 16010,
VERSION_DDNET_NEW_HUD = 16020,
VERSION_DDNET_MULTI_LASER = 16040,
};
#endif

View file

@ -10,6 +10,7 @@
#include <game/mapitems.h>
#include <game/client/gameclient.h>
#include <game/client/laser_data.h>
#include <game/client/projectile_data.h>
#include <game/client/render.h>
@ -237,18 +238,52 @@ void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent,
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, QuadOffset, Pos.x, Pos.y - Size * 0.75f);
}
void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted)
void CItems::RenderLaser(const CLaserData *pCurrent, bool IsPredicted)
{
int Type = clamp(pCurrent->m_Type, -1, NUM_LASERTYPES - 1);
ColorRGBA RGB;
vec2 Pos = vec2(pCurrent->m_X, pCurrent->m_Y);
vec2 From = vec2(pCurrent->m_FromX, pCurrent->m_FromY);
vec2 Pos = pCurrent->m_To;
vec2 From = pCurrent->m_From;
float Len = distance(Pos, From);
RGB = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClLaserOutlineColor));
int ColorIn, ColorOut;
switch(Type)
{
case LASERTYPE_RIFLE:
ColorOut = g_Config.m_ClLaserRifleOutlineColor;
ColorIn = g_Config.m_ClLaserRifleInnerColor;
break;
case LASERTYPE_SHOTGUN:
ColorOut = g_Config.m_ClLaserShotgunOutlineColor;
ColorIn = g_Config.m_ClLaserShotgunInnerColor;
break;
case LASERTYPE_DOOR:
ColorOut = g_Config.m_ClLaserDoorOutlineColor;
ColorIn = g_Config.m_ClLaserDoorInnerColor;
break;
case LASERTYPE_FREEZE:
ColorOut = g_Config.m_ClLaserFreezeOutlineColor;
ColorIn = g_Config.m_ClLaserFreezeInnerColor;
break;
default:
ColorOut = g_Config.m_ClLaserRifleOutlineColor;
ColorIn = g_Config.m_ClLaserRifleInnerColor;
}
RGB = color_cast<ColorRGBA>(ColorHSLA(ColorOut));
ColorRGBA OuterColor(RGB.r, RGB.g, RGB.b, 1.0f);
RGB = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClLaserInnerColor));
RGB = color_cast<ColorRGBA>(ColorHSLA(ColorIn));
ColorRGBA InnerColor(RGB.r, RGB.g, RGB.b, 1.0f);
int TuneZone = GameClient()->m_GameWorld.m_WorldConfig.m_UseTuneZones ? Collision()->IsTune(Collision()->GetMapIndex(From)) : 0;
bool IsOtherTeam = (pCurrent->m_ExtraInfo && pCurrent->m_Owner >= 0 && m_pClient->IsOtherTeam(pCurrent->m_Owner));
float Alpha = 1.f;
if(IsOtherTeam)
{
Alpha = g_Config.m_ClShowOthersAlpha / 100.0f;
}
vec2 Dir;
if(Len > 0)
@ -271,7 +306,7 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted)
Graphics()->QuadsBegin();
// do outline
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, Alpha);
Out = vec2(Dir.y, -Dir.x) * (7.0f * Ia);
IGraphics::CFreeformItem Freeform(
@ -283,7 +318,7 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted)
// do inner
Out = vec2(Dir.y, -Dir.x) * (5.0f * Ia);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); // center
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, Alpha); // center
Freeform = IGraphics::CFreeformItem(
From.x - Out.x, From.y - Out.y,
@ -300,9 +335,9 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted)
int CurParticle = (Client()->GameTick(g_Config.m_ClDummy) % 3);
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_aSpriteParticleSplat[CurParticle]);
Graphics()->QuadsSetRotation(Client()->GameTick(g_Config.m_ClDummy));
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, Alpha);
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, m_aParticleSplatOffset[CurParticle], Pos.x, Pos.y);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, Alpha);
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, m_aParticleSplatOffset[CurParticle], Pos.x, Pos.y, 20.f / 24.f, 20.f / 24.f);
}
}
@ -341,8 +376,7 @@ void CItems::OnRender()
auto *const pLaser = dynamic_cast<CLaser *>(pEnt);
if(!pLaser || pLaser->GetOwner() < 0 || !GameClient()->m_aClients[pLaser->GetOwner()].m_IsPredictedLocal)
continue;
CNetObj_Laser Data;
pLaser->FillInfo(&Data);
CLaserData Data = pLaser->GetData();
RenderLaser(&Data, true);
}
for(auto *pPickup = (CPickup *)GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_PICKUP); pPickup; pPickup = (CPickup *)pPickup->NextEntity())
@ -419,7 +453,7 @@ void CItems::OnRender()
if(pPrev)
RenderPickup((const CNetObj_Pickup *)pPrev, (const CNetObj_Pickup *)pData);
}
else if(Item.m_Type == NETOBJTYPE_LASER)
else if(Item.m_Type == NETOBJTYPE_LASER || Item.m_Type == NETOBJTYPE_DDNETLASER)
{
if(UsePredicted)
{
@ -427,7 +461,16 @@ void CItems::OnRender()
if(pLaser && pLaser->GetOwner() >= 0 && GameClient()->m_aClients[pLaser->GetOwner()].m_IsPredictedLocal)
continue;
}
CNetObj_Laser Laser = *((const CNetObj_Laser *)pData);
CLaserData Data;
if(Item.m_Type == NETOBJTYPE_LASER)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pData, &GameClient()->m_GameWorld);
}
else
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pData, &GameClient()->m_GameWorld);
}
if(pEntEx)
{
@ -435,31 +478,35 @@ void CItems::OnRender()
{
if(Inactive && BlinkingLight)
continue;
Laser.m_StartTick = DraggerStartTick;
Data.m_StartTick = DraggerStartTick;
Data.m_Type = LASERTYPE_FREEZE;
}
if(pEntEx->m_EntityClass >= ENTITYCLASS_GUN_NORMAL && pEntEx->m_EntityClass <= ENTITYCLASS_GUN_UNFREEZE)
{
if(Inactive && BlinkingGun)
continue;
Laser.m_StartTick = GunStartTick;
Data.m_StartTick = GunStartTick;
Data.m_Type = pEntEx->m_EntityClass == ENTITYCLASS_GUN_FREEZE ? LASERTYPE_FREEZE : LASERTYPE_DOOR;
}
if(pEntEx->m_EntityClass >= ENTITYCLASS_DRAGGER_WEAK && pEntEx->m_EntityClass <= ENTITYCLASS_DRAGGER_STRONG)
{
if(Inactive && BlinkingDragger)
continue;
Laser.m_StartTick = DraggerStartTick;
Data.m_StartTick = DraggerStartTick;
Data.m_Type = LASERTYPE_DOOR;
}
if(pEntEx->m_EntityClass == ENTITYCLASS_DOOR)
{
if(Inactive || IsSuper)
{
Laser.m_FromX = Laser.m_X;
Laser.m_FromY = Laser.m_Y;
Data.m_From.x = Data.m_To.x;
Data.m_From.y = Data.m_To.y;
}
Laser.m_StartTick = Client()->GameTick(g_Config.m_ClDummy);
Data.m_StartTick = Client()->GameTick(g_Config.m_ClDummy);
Data.m_Type = LASERTYPE_DOOR;
}
}
RenderLaser(&Laser);
RenderLaser(&Data);
}
}

View file

@ -6,13 +6,14 @@
#include <game/generated/protocol.h>
class CProjectileData;
class CLaserData;
class CItems : public CComponent
{
void RenderProjectile(const CProjectileData *pCurrent, int ItemID);
void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted = false);
void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData);
void RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted = false);
void RenderLaser(const CLaserData *pCurrent, bool IsPredicted = false);
int m_ItemsQuadContainerIndex;

View file

@ -315,7 +315,7 @@ int CMenus::DoButton_CheckBox_Common(const void *pID, const char *pText, const c
return UI()->DoButtonLogic(pID, 0, pRect);
}
void CMenus::DoLaserPreview(const CUIRect *pRect, const ColorHSLA LaserOutlineColor, const ColorHSLA LaserInnerColor)
void CMenus::DoLaserPreview(const CUIRect *pRect, const ColorHSLA LaserOutlineColor, const ColorHSLA LaserInnerColor, const int LaserType)
{
ColorRGBA LaserRGB;
CUIRect Section = *pRect;
@ -356,12 +356,34 @@ void CMenus::DoLaserPreview(const CUIRect *pRect, const ColorHSLA LaserOutlineCo
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->QuadsEnd();
switch(LaserType)
{
case LASERTYPE_RIFLE:
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponLaser);
Graphics()->QuadsBegin();
RenderTools()->SelectSprite(SPRITE_WEAPON_LASER_BODY);
Graphics()->QuadsBegin();
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->DrawSprite(Section.x + 30.0f, Section.y + Section.h / 2.0f, 60.0f);
Graphics()->QuadsEnd();
break;
case LASERTYPE_SHOTGUN:
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponShotgun);
RenderTools()->SelectSprite(SPRITE_WEAPON_SHOTGUN_BODY);
Graphics()->QuadsBegin();
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->DrawSprite(Section.x + 30.0f, Section.y + Section.h / 2.0f, 60.0f);
Graphics()->QuadsEnd();
break;
default:
Graphics()->QuadsBegin();
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
QuadItem = IGraphics::CQuadItem(From.x, From.y, 24, 24);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
QuadItem = IGraphics::CQuadItem(From.x, From.y, 20, 20);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->QuadsEnd();
}
}
ColorHSLA CMenus::DoLine_ColorPicker(CButtonContainer *pResetID, const float LineSize, const float WantedPickerPosition, const float LabelSize, const float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, const ColorRGBA DefaultColor, bool CheckBoxSpacing, bool UseCheckBox, int *pCheckBoxValue)

View file

@ -87,8 +87,9 @@ class CMenus : public CComponent
int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
int DoButton_CheckBoxAutoVMarginAndSet(const void *pID, const char *pText, int *pValue, CUIRect *pRect, float VMargin);
int DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
ColorHSLA DoLine_ColorPicker(CButtonContainer *pResetID, float LineSize, float WantedPickerPosition, float LabelSize, float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, ColorRGBA DefaultColor, bool CheckBoxSpacing = true, bool UseCheckBox = false, int *pCheckBoxValue = nullptr);
void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor);
void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor, const int LaserType);
int DoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, bool UseScroll, int Current, int Min, int Max, int Step, float Scale, bool IsHex, float Round, ColorRGBA *pColor);
int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect);

View file

@ -2966,18 +2966,34 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
{
MainView.VSplitMid(&LeftView, &RightView);
// ***** Laser ***** //
// ***** Weapons ***** //
LeftView.HSplitTop(HeadlineAndVMargin, &Label, &LeftView);
UI()->DoLabel(&Label, Localize("Laser"), HeadlineFontSize, TEXTALIGN_LEFT);
UI()->DoLabel(&Label, Localize("Weapons"), HeadlineFontSize, TEXTALIGN_LEFT);
// General laser settings
LeftView.HSplitTop(SectionTotalMargin + 2 * ColorPickerLineSize, &Section, &LeftView);
// General weapon laser settings
LeftView.HSplitTop(SectionTotalMargin + 4 * ColorPickerLineSize, &Section, &LeftView);
Section.Margin(SectionMargin, &Section);
static CButtonContainer s_LaserOutResetID, s_LaserInResetID;
static CButtonContainer s_LaserRifleOutResetID, s_LaserRifleInResetID, s_LaserShotgunOutResetID, s_LaserShotgunInResetID;
ColorHSLA LaserOutlineColor = DoLine_ColorPicker(&s_LaserOutResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Laser Outline Color"), &g_Config.m_ClLaserOutlineColor, ColorRGBA(0.074402f, 0.074402f, 0.247166f, 1.0f), false);
ColorHSLA LaserInnerColor = DoLine_ColorPicker(&s_LaserInResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Laser Inner Color"), &g_Config.m_ClLaserInnerColor, ColorRGBA(0.498039f, 0.498039f, 1.0f, 1.0f), false);
ColorHSLA LaserRifleOutlineColor = DoLine_ColorPicker(&s_LaserRifleOutResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Rifle Laser Outline Color"), &g_Config.m_ClLaserRifleOutlineColor, ColorRGBA(0.074402f, 0.074402f, 0.247166f, 1.0f), false);
ColorHSLA LaserRifleInnerColor = DoLine_ColorPicker(&s_LaserRifleInResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Rifle Laser Inner Color"), &g_Config.m_ClLaserRifleInnerColor, ColorRGBA(0.498039f, 0.498039f, 1.0f, 1.0f), false);
ColorHSLA LaserShotgunOutlineColor = DoLine_ColorPicker(&s_LaserShotgunOutResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Shotgun Laser Outline Color"), &g_Config.m_ClLaserShotgunOutlineColor, ColorRGBA(0.125490f, 0.098039f, 0.043137f, 1.0f), false);
ColorHSLA LaserShotgunInnerColor = DoLine_ColorPicker(&s_LaserShotgunInResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Shotgun Laser Inner Color"), &g_Config.m_ClLaserShotgunInnerColor, ColorRGBA(0.764705f, 0.505882f, 0.0f, 1.0f), false);
// ***** Entities ***** //
LeftView.HSplitTop(HeadlineAndVMargin, &Label, &LeftView);
UI()->DoLabel(&Label, Localize("Entities"), HeadlineFontSize, TEXTALIGN_LEFT);
// General entity laser settings
LeftView.HSplitTop(SectionTotalMargin + 4 * ColorPickerLineSize, &Section, &LeftView);
Section.Margin(SectionMargin, &Section);
static CButtonContainer s_LaserDoorOutResetID, s_LaserDoorInResetID, s_LaserFreezeOutResetID, s_LaserFreezeInResetID;
ColorHSLA LaserDoorOutlineColor = DoLine_ColorPicker(&s_LaserDoorOutResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Door Laser Outline Color"), &g_Config.m_ClLaserDoorOutlineColor, ColorRGBA(0.0f, 0.129411f, 0.094117f, 1.0f), false);
ColorHSLA LaserDoorInnerColor = DoLine_ColorPicker(&s_LaserDoorInResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Door Laser Inner Color"), &g_Config.m_ClLaserDoorInnerColor, ColorRGBA(0.262745f, 0.760784f, 0.639215f, 1.0f), false);
ColorHSLA LaserFreezeOutlineColor = DoLine_ColorPicker(&s_LaserFreezeOutResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Freeze Laser Outline Color"), &g_Config.m_ClLaserFreezeOutlineColor, ColorRGBA(0.192156f, 0.031372f, 0.074509f, 1.0f), false);
ColorHSLA LaserFreezeInnerColor = DoLine_ColorPicker(&s_LaserFreezeInResetID, ColorPickerLineSize, LeftViewColorPickerPosition, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Freeze Laser Inner Color"), &g_Config.m_ClLaserFreezeInnerColor, ColorRGBA(0.760784f, 0.262745f, 0.403921f, 1.0f), false);
// ***** Laser Preview ***** //
RightView.HSplitTop(HeadlineAndVMargin, &Label, &RightView);
@ -2985,8 +3001,19 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
RightView.HSplitTop(SectionTotalMargin + 50.0f, &Section, &RightView);
Section.Margin(SectionMargin, &Section);
DoLaserPreview(&Section, LaserRifleOutlineColor, LaserRifleInnerColor, LASERTYPE_RIFLE);
DoLaserPreview(&Section, LaserOutlineColor, LaserInnerColor);
RightView.HSplitTop(SectionTotalMargin + 50.0f, &Section, &RightView);
Section.Margin(SectionMargin, &Section);
DoLaserPreview(&Section, LaserShotgunOutlineColor, LaserShotgunInnerColor, LASERTYPE_SHOTGUN);
RightView.HSplitTop(SectionTotalMargin + 50.0f, &Section, &RightView);
Section.Margin(SectionMargin, &Section);
DoLaserPreview(&Section, LaserDoorOutlineColor, LaserDoorInnerColor, LASERTYPE_DOOR);
RightView.HSplitTop(SectionTotalMargin + 50.0f, &Section, &RightView);
Section.Margin(SectionMargin, &Section);
DoLaserPreview(&Section, LaserFreezeOutlineColor, LaserFreezeInnerColor, LASERTYPE_DOOR);
}
}

View file

@ -3311,7 +3311,7 @@ void CGameClient::SnapCollectEntities()
const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, Index, &Item);
if(Item.m_Type == NETOBJTYPE_ENTITYEX)
vItemEx.push_back({Item, pData, 0});
else if(Item.m_Type == NETOBJTYPE_PICKUP || Item.m_Type == NETOBJTYPE_LASER || Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
else if(Item.m_Type == NETOBJTYPE_PICKUP || Item.m_Type == NETOBJTYPE_LASER || Item.m_Type == NETOBJTYPE_DDNETLASER || Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
vItemData.push_back({Item, pData, 0});
}

View file

@ -0,0 +1,40 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "laser_data.h"
#include <engine/shared/snapshot.h>
#include <game/client/prediction/gameworld.h>
#include <game/generated/protocol.h>
#include <game/collision.h>
CLaserData ExtractLaserInfo(const CNetObj_Laser *pLaser, CGameWorld *pGameWorld)
{
CLaserData Result = {vec2(0, 0)};
Result.m_From.x = pLaser->m_FromX;
Result.m_From.y = pLaser->m_FromY;
Result.m_To.x = pLaser->m_X;
Result.m_To.y = pLaser->m_Y;
Result.m_StartTick = pLaser->m_StartTick;
Result.m_ExtraInfo = false;
Result.m_Owner = -1;
Result.m_Type = -1;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_From)) : 0;
return Result;
}
CLaserData ExtractLaserInfoDDNet(const CNetObj_DDNetLaser *pLaser, CGameWorld *pGameWorld)
{
CLaserData Result = {vec2(0, 0)};
Result.m_From.x = pLaser->m_FromX;
Result.m_From.y = pLaser->m_FromY;
Result.m_To.x = pLaser->m_ToX;
Result.m_To.y = pLaser->m_ToY;
Result.m_StartTick = pLaser->m_StartTick;
Result.m_ExtraInfo = true;
Result.m_Owner = pLaser->m_Owner;
Result.m_Type = pLaser->m_Type;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_From)) : 0;
return Result;
}

View file

@ -0,0 +1,28 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_LASER_DATA_H
#define GAME_CLIENT_LASER_DATA_H
#include <base/vmath.h>
struct CNetObj_Laser;
struct CNetObj_DDNetLaser;
class CLaserData
{
public:
vec2 m_From;
vec2 m_To;
int m_StartTick;
bool m_ExtraInfo;
// The rest is only set if m_ExtraInfo is true.
int m_Owner;
int m_Type;
// TuneZone is introduced locally
int m_TuneZone;
};
CLaserData ExtractLaserInfo(const CNetObj_Laser *pLaser, class CGameWorld *pGameWorld);
CLaserData ExtractLaserInfoDDNet(const CNetObj_DDNetLaser *pLaser, class CGameWorld *pGameWorld);
#endif // GAME_CLIENT_LASER_DATA_H

View file

@ -2,6 +2,7 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "laser.h"
#include "character.h"
#include <game/client/laser_data.h>
#include <game/collision.h>
#include <game/generated/protocol.h>
#include <game/mapitems.h>
@ -185,13 +186,11 @@ void CLaser::Tick()
}
}
CLaser::CLaser(CGameWorld *pGameWorld, int ID, CNetObj_Laser *pLaser) :
CLaser::CLaser(CGameWorld *pGameWorld, int ID, CLaserData *pLaser) :
CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
{
m_Pos.x = pLaser->m_X;
m_Pos.y = pLaser->m_Y;
m_From.x = pLaser->m_FromX;
m_From.y = pLaser->m_FromY;
m_Pos = pLaser->m_To;
m_From = pLaser->m_From;
m_EvalTick = pLaser->m_StartTick;
m_TuneZone = GameWorld()->m_WorldConfig.m_UseTuneZones ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;
m_Owner = -2;
@ -204,7 +203,7 @@ CLaser::CLaser(CGameWorld *pGameWorld, int ID, CNetObj_Laser *pLaser) :
m_Dir = normalize(m_Dir);
else
m_Energy = 0;
m_Type = WEAPON_LASER;
m_Type = pLaser->m_Type == LASERTYPE_SHOTGUN ? WEAPON_SHOTGUN : WEAPON_LASER;
m_PrevPos = m_From;
m_ID = ID;
}
@ -229,3 +228,18 @@ bool CLaser::Match(CLaser *pLaser)
const float DirError = distance(normalize(OtherDiff) * length(ThisDiff), ThisDiff);
return DirError <= 2.f;
}
CLaserData CLaser::GetData() const
{
CLaserData Result;
Result.m_From.x = m_From.x;
Result.m_From.y = m_From.y;
Result.m_To.x = m_Pos.x;
Result.m_To.y = m_Pos.y;
Result.m_StartTick = m_EvalTick;
Result.m_ExtraInfo = true;
Result.m_Owner = m_Owner;
Result.m_Type = m_Type == WEAPON_SHOTGUN ? LASERTYPE_SHOTGUN : LASERTYPE_RIFLE;
Result.m_TuneZone = m_TuneZone;
return Result;
}

View file

@ -5,6 +5,8 @@
#include <game/client/prediction/entity.h>
class CLaserData;
class CLaser : public CEntity
{
friend class CGameWorld;
@ -17,9 +19,10 @@ public:
const vec2 &GetFrom() { return m_From; }
const int &GetOwner() { return m_Owner; }
const int &GetEvalTick() { return m_EvalTick; }
CLaser(CGameWorld *pGameWorld, int ID, CNetObj_Laser *pLaser);
CLaser(CGameWorld *pGameWorld, int ID, CLaserData *pLaser);
void FillInfo(CNetObj_Laser *pLaser);
bool Match(CLaser *pLaser);
CLaserData GetData() const;
protected:
bool HitCharacter(vec2 From, vec2 To);

View file

@ -9,6 +9,7 @@
#include "entity.h"
#include <algorithm>
#include <engine/shared/config.h>
#include <game/client/laser_data.h>
#include <game/client/projectile_data.h>
#include <game/mapitems.h>
#include <utility>
@ -480,9 +481,18 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const C
CEntity *pEnt = new CPickup(NetPickup);
InsertEntity(pEnt, true);
}
else if(ObjType == NETOBJTYPE_LASER && m_WorldConfig.m_PredictWeapons)
else if((ObjType == NETOBJTYPE_LASER || ObjType == NETOBJTYPE_DDNETLASER) && m_WorldConfig.m_PredictWeapons)
{
CLaser NetLaser = CLaser(this, ObjID, (CNetObj_Laser *)pObjData);
CLaserData Data;
if(ObjType == NETOBJTYPE_PROJECTILE)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pObjData, this);
}
else
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pObjData, this);
}
CLaser NetLaser = CLaser(this, ObjID, &Data);
CLaser *pMatching = 0;
if(CLaser *pLaser = dynamic_cast<CLaser *>(GetEntity(ObjID, ENTTYPE_LASER)))
if(NetLaser.Match(pLaser))
@ -632,7 +642,25 @@ CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
}
return 0;
}
case NETOBJTYPE_LASER: FindType(ENTTYPE_LASER, CLaser, CNetObj_Laser);
case NETOBJTYPE_LASER:
case NETOBJTYPE_DDNETLASER:
{
CLaserData Data;
if(ObjType == NETOBJTYPE_LASER)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pObjData, this);
}
else
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pObjData, this);
}
CLaser *pEnt = (CLaser *)GetEntity(ObjID, ENTTYPE_LASER);
if(pEnt && CLaser(this, ObjID, &Data).Match(pEnt))
{
return pEnt;
}
return 0;
}
case NETOBJTYPE_PICKUP: FindType(ENTTYPE_PICKUP, CPickup, CNetObj_Pickup);
}
return 0;

View file

@ -313,6 +313,23 @@ void CLaser::Snap(int SnappingClient)
if(SnappingClient != SERVER_DEMO_CLIENT && !CmaskIsSet(TeamMask, SnappingClient))
return;
if(GameServer()->GetClientVersion(SnappingClient) >= VERSION_DDNET_MULTI_LASER)
{
CNetObj_DDNetLaser *pObj = static_cast<CNetObj_DDNetLaser *>(Server()->SnapNewItem(NETOBJTYPE_DDNETLASER, GetID(), sizeof(CNetObj_DDNetLaser)));
if(!pObj)
return;
pObj->m_ToX = (int)m_Pos.x;
pObj->m_ToY = (int)m_Pos.y;
pObj->m_FromX = (int)m_From.x;
pObj->m_FromY = (int)m_From.y;
pObj->m_StartTick = m_EvalTick;
pObj->m_Owner = m_Owner;
pObj->m_Type = m_Type == WEAPON_LASER ? LASERTYPE_RIFLE : m_Type == WEAPON_SHOTGUN ? LASERTYPE_SHOTGUN : -1;
}
else
{
CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
if(!pObj)
return;
@ -322,6 +339,7 @@ void CLaser::Snap(int SnappingClient)
pObj->m_FromX = (int)m_From.x;
pObj->m_FromY = (int)m_From.y;
pObj->m_StartTick = m_EvalTick;
}
}
void CLaser::SwapClients(int Client1, int Client2)