mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 18:18:18 +00:00
Merge branch 'master' of git://github.com/GreYFoXGTi/DDRace into teams
This commit is contained in:
commit
faf743b02a
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -23,4 +23,5 @@ DDRace_Trunk_d*
|
|||
*.log
|
||||
*.exe
|
||||
*.res
|
||||
*.dll
|
||||
*.dll
|
||||
*.patch
|
Binary file not shown.
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 272 KiB |
|
@ -190,8 +190,8 @@ Language
|
|||
Lht.
|
||||
== Světel.
|
||||
|
||||
Loading
|
||||
== Nahrávám
|
||||
Loading DDRace Client
|
||||
== Nahrávám DDRace Client
|
||||
|
||||
MOTD
|
||||
== MOTD
|
||||
|
|
|
@ -200,8 +200,8 @@ Language
|
|||
Lht.
|
||||
== Licht
|
||||
|
||||
Loading
|
||||
== Laden
|
||||
Loading DDRace Client
|
||||
== Laden DDRace Client
|
||||
|
||||
MOTD
|
||||
== MOTD
|
||||
|
|
|
@ -190,8 +190,8 @@ Language
|
|||
Lht.
|
||||
== Lum.
|
||||
|
||||
Loading
|
||||
== Chargement
|
||||
Loading DDRace Client
|
||||
== Chargement DDRace Client
|
||||
|
||||
MOTD
|
||||
== MOTD
|
||||
|
|
|
@ -190,8 +190,8 @@ Language
|
|||
Lht.
|
||||
== Hell.
|
||||
|
||||
Loading
|
||||
== Laden
|
||||
Loading DDRace Client
|
||||
== Laden DDRace Client
|
||||
|
||||
MOTD
|
||||
== Nachricht des Tages
|
||||
|
|
|
@ -187,8 +187,8 @@ Language
|
|||
Lht.
|
||||
== Lum.
|
||||
|
||||
Loading
|
||||
== Caricamento
|
||||
Loading DDRace Client
|
||||
== Caricamento DDRace Client
|
||||
|
||||
MOTD
|
||||
== MDG
|
||||
|
|
|
@ -201,8 +201,8 @@ Language
|
|||
Lht.
|
||||
== Luz
|
||||
|
||||
Loading
|
||||
== Carregando
|
||||
Loading DDRace Client
|
||||
== Carregando DDRace Client
|
||||
|
||||
MOTD
|
||||
== MOTD
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
##### translated strings #####
|
||||
|
||||
%d of %d servers, %d players
|
||||
|
@ -190,8 +190,8 @@ Language
|
|||
Lht.
|
||||
== L
|
||||
|
||||
Loading
|
||||
== Загрузка
|
||||
Loading DDRace Client
|
||||
== Загрузка DDRace Client
|
||||
|
||||
MOTD
|
||||
== MOTD
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
##### translated strings #####
|
||||
|
||||
%d of %d servers, %d players
|
||||
|
@ -187,8 +187,8 @@ Language
|
|||
Lht.
|
||||
== Ljusstyrka
|
||||
|
||||
Loading
|
||||
== Laddar
|
||||
Loading DDRace Client
|
||||
== Laddar DDRace Client
|
||||
|
||||
MOTD
|
||||
== Meddelande
|
||||
|
|
Binary file not shown.
|
@ -9,7 +9,6 @@
|
|||
MACRO_CONFIG_INT(SvShotgunBulletSound, sv_shotgun_bullet_sound, 0, 0, 1, CFGFLAG_SERVER, "Annoying Shotgun sound on/off")
|
||||
MACRO_CONFIG_INT(SvEndlessSuperHook, sv_endless_super_hook, 0, 0, 1, CFGFLAG_SERVER, "Endless hook for super players on/off")
|
||||
MACRO_CONFIG_INT(SvEmotionalTees, sv_emotional_tees, 1, 0, 1, CFGFLAG_SERVER, "Emotional Tees on/off")
|
||||
MACRO_CONFIG_INT(SvOldShotgun, sv_old_shotgun, 0, 0, 1, CFGFLAG_SERVER, "Makes Shotgun laser pull towards the shooter, rather than the last bounce origin")
|
||||
MACRO_CONFIG_INT(SvReconnectTime,sv_reconnect_time,5,0,9999,CFGFLAG_SERVER,"how much time between leaves and joins")
|
||||
MACRO_CONFIG_INT(SvVoteKickTimeDelay,sv_vote_kick_delay,0,0,9999,CFGFLAG_SERVER,"how much time between kick votes")
|
||||
MACRO_CONFIG_INT(SvVoteKickBanTime,sv_vote_kick_bantime, 300, 0, 9999, CFGFLAG_SERVER," ")
|
||||
|
|
|
@ -664,7 +664,7 @@ void CMenus::RenderLoading(float Percent)
|
|||
Graphics()->QuadsEnd();
|
||||
|
||||
|
||||
const char *pCaption = Localize("Loading");
|
||||
const char *pCaption = Localize("Loading DDRace Client");
|
||||
|
||||
tw = TextRender()->TextWidth(0, 48.0f, pCaption, -1);
|
||||
CUIRect r;
|
||||
|
|
|
@ -73,6 +73,7 @@ public:
|
|||
void RenderTelemap(CTeleTile *pTele, int w, int h, float Scale);
|
||||
void RenderSpeedupmap(CSpeedupTile *pTele, int w, int h, float Scale);
|
||||
void RenderFrontmap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int Flags);
|
||||
void RenderSwitchmap(CTeleTile *pSwitch, int w, int h, float Scale);
|
||||
|
||||
// helpers
|
||||
void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
|
||||
|
|
|
@ -369,4 +369,44 @@ void CRenderTools::RenderSpeedupmap(CSpeedupTile *pSpeedup, int w, int h, float
|
|||
}
|
||||
|
||||
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
||||
}
|
||||
}
|
||||
|
||||
void CRenderTools::RenderSwitchmap(CTeleTile *pSwitch, int w, int h, float Scale)
|
||||
{
|
||||
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
||||
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
||||
|
||||
int StartY = (int)(ScreenY0/Scale)-1;
|
||||
int StartX = (int)(ScreenX0/Scale)-1;
|
||||
int EndY = (int)(ScreenY1/Scale)+1;
|
||||
int EndX = (int)(ScreenX1/Scale)+1;
|
||||
|
||||
for(int y = StartY; y < EndY; y++)
|
||||
for(int x = StartX; x < EndX; x++)
|
||||
{
|
||||
int mx = x;
|
||||
int my = y;
|
||||
|
||||
|
||||
if(mx<0)
|
||||
continue; // mx = 0;
|
||||
if(mx>=w)
|
||||
continue; // mx = w-1;
|
||||
if(my<0)
|
||||
continue; // my = 0;
|
||||
if(my>=h)
|
||||
continue; // my = h-1;
|
||||
|
||||
int c = mx + my*w;
|
||||
|
||||
unsigned char Index = pSwitch[c].m_Number;
|
||||
if(Index)
|
||||
{
|
||||
char aBuf[16];
|
||||
str_format(aBuf, sizeof(aBuf), "%d", Index);
|
||||
UI()->TextRender()->Text(0, mx*Scale-2, my*Scale-4, Scale-5, aBuf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ void CCollision::Init(class CLayers *pLayers)
|
|||
m_pTele = static_cast<CTeleTile *>(m_pLayers->Map()->GetData(m_pLayers->TeleLayer()->m_Tele));
|
||||
if(m_pLayers->SpeedupLayer())
|
||||
m_pSpeedup = static_cast<CSpeedupTile *>(m_pLayers->Map()->GetData(m_pLayers->SpeedupLayer()->m_Speedup));
|
||||
if(m_pLayers->SwitchLayer())
|
||||
m_pSwitch = static_cast<CTeleTile *>(m_pLayers->Map()->GetData(m_pLayers->SwitchLayer()->m_Switch));
|
||||
if(m_pLayers->FrontLayer())
|
||||
{
|
||||
m_pFront = static_cast<CTile *>(m_pLayers->Map()->GetData(m_pLayers->FrontLayer()->m_Front));
|
||||
|
|
|
@ -9,6 +9,10 @@ class CCollision
|
|||
int m_Width;
|
||||
int m_Height;
|
||||
class CLayers *m_pLayers;
|
||||
class CTeleTile *m_pTele;
|
||||
class CSpeedupTile *m_pSpeedup;
|
||||
class CTile *m_pFront;
|
||||
class CTeleTile *m_pSwitch;
|
||||
|
||||
public:
|
||||
enum
|
||||
|
@ -62,9 +66,10 @@ public:
|
|||
|
||||
vec2 CpSpeed(int index);
|
||||
|
||||
class CTeleTile *m_pTele;
|
||||
class CSpeedupTile *m_pSpeedup;
|
||||
class CTile *m_pFront;
|
||||
class CTeleTile *TeleLayer() { return m_pTele; }
|
||||
//class CSpeedupTile *SpeedupLayer() { return m_pSpeedup; }
|
||||
//class CTile *FrontLayer() { m_pFront; }
|
||||
class CTeleTile *SwitchLayer() { return m_pSwitch; }
|
||||
class CLayers *Layers() { return m_pLayers; }
|
||||
};
|
||||
|
||||
|
|
|
@ -788,7 +788,7 @@ void CEditor::DoToolbar(CUIRect ToolBar)
|
|||
CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
|
||||
|
||||
// no border for tele layer and speedup
|
||||
if(pT && (pT->m_Tele || pT->m_Speedup))
|
||||
if(pT && (pT->m_Tele || pT->m_Speedup || pT->m_Switch || pT->m_Front))
|
||||
pT = 0;
|
||||
|
||||
if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, Localize("Border")))
|
||||
|
@ -817,6 +817,15 @@ void CEditor::DoToolbar(CUIRect ToolBar)
|
|||
static int s_SpeedupPopupId = 0;
|
||||
UiInvokePopupMenu(&s_SpeedupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 43, PopupSpeedup);
|
||||
}
|
||||
|
||||
TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom);
|
||||
TB_Bottom.VSplitLeft(60.0f, &Button, &TB_Bottom);
|
||||
static int s_SwitchButton = 0;
|
||||
if(DoButton_Ex(&s_SwitchButton, "Switcher", (pS && pS->m_Switch)?0:-1, &Button, 0, "Switcher", CUI::CORNER_ALL))
|
||||
{
|
||||
static int s_SwitchPopupId = 0;
|
||||
UiInvokePopupMenu(&s_SwitchPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 23, PopupSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
|
||||
|
@ -1135,7 +1144,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
|
|||
//UI()->ClipEnable(&view);
|
||||
}
|
||||
|
||||
// render the game, tele, speedup and front above everything else
|
||||
// render the game, tele, speedup, front and switch above everything else
|
||||
if(m_Map.m_pGameGroup->m_Visible)
|
||||
{
|
||||
m_Map.m_pGameGroup->MapScreen();
|
||||
|
@ -1147,6 +1156,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
|
|||
m_Map.m_pTeleLayer->Render();
|
||||
if(m_Map.m_pSpeedupLayer && m_Map.m_pSpeedupLayer->m_Visible)
|
||||
m_Map.m_pSpeedupLayer->Render();
|
||||
if(m_Map.m_pSwitchLayer && m_Map.m_pSwitchLayer->m_Visible)
|
||||
m_Map.m_pSwitchLayer->Render();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1798,7 +1809,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
|
|||
if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button,
|
||||
BUTTON_CONTEXT, Localize("Select layer. Right click for properties."), CUI::CORNER_R))
|
||||
{
|
||||
if(m_Map.m_lGroups[g]->m_lLayers[i] == m_Map.m_pTeleLayer || m_Map.m_lGroups[g]->m_lLayers[i] == m_Map.m_pSpeedupLayer)
|
||||
if(m_Map.m_lGroups[g]->m_lLayers[i] == m_Map.m_pTeleLayer || m_Map.m_lGroups[g]->m_lLayers[i] == m_Map.m_pSpeedupLayer || m_Map.m_lGroups[g]->m_lLayers[i] == m_Map.m_pSwitchLayer)//Clear the brush on entering tele/speedup/switch layer
|
||||
m_Brush.Clear();
|
||||
m_SelectedLayer = i;
|
||||
m_SelectedGroup = g;
|
||||
|
@ -3026,6 +3037,13 @@ void CEditorMap::MakeFrontLayer(CLayer *pLayer)
|
|||
m_pFrontLayer->m_TexId = m_pEditor->ms_EntitiesTexture;
|
||||
}
|
||||
|
||||
void CEditorMap::MakeSwitchLayer(CLayer *pLayer)
|
||||
{
|
||||
m_pSwitchLayer = (CLayerSwitch *)pLayer;
|
||||
m_pSwitchLayer->m_pEditor = m_pEditor;
|
||||
m_pSwitchLayer->m_TexId = m_pEditor->ms_EntitiesTexture;
|
||||
}
|
||||
|
||||
void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
|
||||
{
|
||||
m_pGameGroup = pGroup;
|
||||
|
@ -3045,6 +3063,7 @@ void CEditorMap::Clean()
|
|||
m_pTeleLayer = 0x0;
|
||||
m_pSpeedupLayer = 0x0;
|
||||
m_pFrontLayer = 0x0;
|
||||
m_pSwitchLayer = 0x0;
|
||||
m_pGameGroup = 0x0;
|
||||
}
|
||||
|
||||
|
@ -3079,6 +3098,7 @@ void CEditorMap::CreateDefault(int EntitiesTexture)
|
|||
m_pGameGroup->AddLayer(m_pFrontLayer);
|
||||
m_pTeleLayer = 0x0;
|
||||
m_pSpeedupLayer = 0x0;
|
||||
m_pSwitchLayer = 0x0;
|
||||
}
|
||||
|
||||
void CEditor::Init()
|
||||
|
|
|
@ -266,6 +266,7 @@ public:
|
|||
class CLayerTele *m_pTeleLayer;
|
||||
class CLayerSpeedup *m_pSpeedupLayer;
|
||||
class CLayerFront *m_pFrontLayer;
|
||||
class CLayerSwitch *m_pSwitchLayer;
|
||||
CLayerGroup *m_pGameGroup;
|
||||
|
||||
CEnvelope *NewEnvelope(int Channels)
|
||||
|
@ -323,6 +324,7 @@ public:
|
|||
void MakeTeleLayer(CLayer *pLayer);
|
||||
void MakeSpeedupLayer(CLayer *pLayer);
|
||||
void MakeFrontLayer(CLayer *pLayer);
|
||||
void MakeSwitchLayer(CLayer *pLayer);
|
||||
};
|
||||
|
||||
|
||||
|
@ -390,6 +392,7 @@ public:
|
|||
int m_Tele;
|
||||
int m_Speedup;
|
||||
int m_Front;
|
||||
int m_Switch;
|
||||
int m_Image;
|
||||
int m_Width;
|
||||
int m_Height;
|
||||
|
@ -467,6 +470,20 @@ public:
|
|||
virtual void BrushDraw(CLayer *pBrush, float wx, float wy);
|
||||
};
|
||||
|
||||
class CLayerSwitch : public CLayerTiles
|
||||
{
|
||||
public:
|
||||
CLayerSwitch(int w, int h);
|
||||
~CLayerSwitch();
|
||||
|
||||
CTeleTile *m_pSwitchTile;
|
||||
|
||||
virtual void Resize(int NewW, int NewH);
|
||||
virtual void BrushDraw(CLayer *pBrush, float wx, float wy);
|
||||
virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect);
|
||||
};
|
||||
|
||||
|
||||
class CEditor : public IEditor
|
||||
{
|
||||
class IInput *m_pInput;
|
||||
|
@ -531,6 +548,7 @@ public:
|
|||
ms_pUiGotContext = 0;
|
||||
|
||||
m_TeleNum = 1;
|
||||
m_SwitchNum = 1;
|
||||
|
||||
m_SpeedupForce = 50;
|
||||
m_SpeedupAngle = 0;
|
||||
|
@ -636,6 +654,7 @@ public:
|
|||
static int PopupMenuFile(CEditor *pEditor, CUIRect View);
|
||||
static int PopupTele(CEditor *pEditor, CUIRect View);
|
||||
static int PopupSpeedup(CEditor *pEditor, CUIRect View);
|
||||
static int PopupSwitch(CEditor *pEditor, CUIRect View);
|
||||
|
||||
|
||||
void PopupSelectImageInvoke(int Current, float x, float y);
|
||||
|
@ -669,6 +688,8 @@ public:
|
|||
|
||||
unsigned char m_SpeedupForce;
|
||||
short m_SpeedupAngle;
|
||||
|
||||
unsigned char m_SwitchNum;
|
||||
};
|
||||
|
||||
// make sure to inline this function
|
||||
|
|
|
@ -288,6 +288,8 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
|
|||
Item.m_Flags = 4;
|
||||
else if(pLayer->m_Front)
|
||||
Item.m_Flags = 8;
|
||||
else if(pLayer->m_Switch)
|
||||
Item.m_Flags = 16;
|
||||
else
|
||||
Item.m_Flags = pLayer->m_Game;
|
||||
Item.m_Image = pLayer->m_Image;
|
||||
|
@ -315,6 +317,14 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
|
|||
Item.m_Front = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles);//Thanks Sushi Tee
|
||||
delete[] Tiles;
|
||||
}
|
||||
else if(pLayer->m_Switch)
|
||||
{
|
||||
CTile *Tiles = new CTile[pLayer->m_Width*pLayer->m_Height];
|
||||
mem_zero(Tiles, pLayer->m_Width*pLayer->m_Height*sizeof(CTile));
|
||||
Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), Tiles);
|
||||
Item.m_Switch = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTeleTile), ((CLayerSwitch *)pLayer)->m_pSwitchTile);
|
||||
delete[] Tiles;
|
||||
}
|
||||
else
|
||||
Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles);
|
||||
df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
|
||||
|
@ -537,6 +547,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName)
|
|||
pTiles = new CLayerFront(pTilemapItem->m_Width, pTilemapItem->m_Height);
|
||||
MakeFrontLayer(pTiles);
|
||||
}
|
||||
else if(pTilemapItem->m_Flags&16)
|
||||
{
|
||||
pTiles = new CLayerSwitch(pTilemapItem->m_Width, pTilemapItem->m_Height);
|
||||
MakeSwitchLayer(pTiles);
|
||||
}
|
||||
else
|
||||
{
|
||||
pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height);
|
||||
|
@ -601,6 +616,28 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName)
|
|||
|
||||
DataFile.UnloadData(pTilemapItem->m_Front);
|
||||
}
|
||||
else if(pTiles->m_Switch)
|
||||
{
|
||||
void *pSwitchData = DataFile.GetData(pTilemapItem->m_Switch);
|
||||
mem_copy(((CLayerSwitch*)pTiles)->m_pSwitchTile, pSwitchData, pTiles->m_Width*pTiles->m_Height*sizeof(CTeleTile));
|
||||
|
||||
for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++)
|
||||
{
|
||||
if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_TRIGGER + ENTITY_OFFSET))
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = (ENTITY_TRIGGER + ENTITY_OFFSET);
|
||||
else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_DOOR + ENTITY_OFFSET))
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = (ENTITY_DOOR + ENTITY_OFFSET);
|
||||
else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_LASER_SHORT + ENTITY_OFFSET))
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = (ENTITY_LASER_SHORT + ENTITY_OFFSET);
|
||||
else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_LASER_MIDDLE + ENTITY_OFFSET))
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = (ENTITY_LASER_MIDDLE + ENTITY_OFFSET);
|
||||
else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_LASER_LONG + ENTITY_OFFSET))
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = (ENTITY_LASER_LONG + ENTITY_OFFSET);
|
||||
else
|
||||
((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0;
|
||||
}
|
||||
DataFile.UnloadData(pTilemapItem->m_Switch);
|
||||
}
|
||||
}
|
||||
else if(pLayerItem->m_Type == LAYERTYPE_QUADS)
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@ CLayerTiles::CLayerTiles(int w, int h)
|
|||
m_Tele = 0;
|
||||
m_Speedup = 0;
|
||||
m_Front = 0;
|
||||
m_Switch = 0;
|
||||
|
||||
m_pTiles = new CTile[m_Width*m_Height];
|
||||
mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile));
|
||||
|
@ -64,6 +65,8 @@ void CLayerTiles::Render()
|
|||
m_pEditor->RenderTools()->RenderTelemap(((CLayerTele*)this)->m_pTeleTile, m_Width, m_Height, 32.0f);
|
||||
if(m_Speedup)
|
||||
m_pEditor->RenderTools()->RenderSpeedupmap(((CLayerSpeedup*)this)->m_pSpeedupTile, m_Width, m_Height, 32.0f);
|
||||
if(m_Switch)
|
||||
m_pEditor->RenderTools()->RenderSwitchmap(((CLayerSwitch*)this)->m_pSwitchTile, m_Width, m_Height, 32.0f);
|
||||
}
|
||||
|
||||
int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); }
|
||||
|
@ -177,6 +180,26 @@ int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
|
|||
for(int x = 0; x < r.w; x++)
|
||||
pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x] = ((CLayerSpeedup*)this)->m_pSpeedupTile[(r.y+y)*m_Width+(r.x+x)];
|
||||
}
|
||||
else if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pSwitchLayer)
|
||||
{
|
||||
CLayerSwitch *pGrabbed = new CLayerSwitch(r.w, r.h);
|
||||
pGrabbed->m_pEditor = m_pEditor;
|
||||
pGrabbed->m_TexId = m_TexId;
|
||||
pGrabbed->m_Image = m_Image;
|
||||
|
||||
pBrush->AddLayer(pGrabbed);
|
||||
|
||||
// copy the tiles
|
||||
for(int y = 0; y < r.h; y++)
|
||||
for(int x = 0; x < r.w; x++)
|
||||
pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = m_pTiles[(r.y+y)*m_Width+(r.x+x)];
|
||||
|
||||
// copy the switch data
|
||||
if(!m_pEditor->Input()->KeyPressed(KEY_SPACE))
|
||||
for(int y = 0; y < r.h; y++)
|
||||
for(int x = 0; x < r.w; x++)
|
||||
pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x] = ((CLayerSwitch*)this)->m_pSwitchTile[(r.y+y)*m_Width+(r.x+x)];
|
||||
}
|
||||
else
|
||||
{
|
||||
CLayerTiles *pGrabbed = new CLayerTiles(r.w, r.h);
|
||||
|
@ -244,7 +267,7 @@ void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|||
continue;
|
||||
|
||||
// dont allow tele in and out tiles... same with speedup tile in game layer
|
||||
if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pGameLayer && (l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_BOOST))
|
||||
if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pGameLayer && (l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_BOOST || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_TRIGGER + ENTITY_OFFSET)|| l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_DOOR + ENTITY_OFFSET)))
|
||||
continue;
|
||||
|
||||
m_pTiles[fy*m_Width+fx] = l->m_pTiles[y*l->m_Width+x];
|
||||
|
@ -307,6 +330,11 @@ void CLayerTiles::Resize(int NewW, int NewH)
|
|||
// resize front layer
|
||||
if(m_Game && m_pEditor->m_Map.m_pFrontLayer && (m_pEditor->m_Map.m_pFrontLayer->m_Width != NewW || m_pEditor->m_Map.m_pFrontLayer->m_Height != NewH))
|
||||
m_pEditor->m_Map.m_pFrontLayer->Resize(NewW, NewH);
|
||||
|
||||
// resize switch layer if available
|
||||
if(m_Game && m_pEditor->m_Map.m_pSwitchLayer && (m_pEditor->m_Map.m_pSwitchLayer->m_Width != NewW || m_pEditor->m_Map.m_pSwitchLayer->m_Height != NewH))
|
||||
m_pEditor->m_Map.m_pSwitchLayer->Resize(NewW, NewH);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -316,7 +344,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
|||
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
|
||||
|
||||
bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty();
|
||||
if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this)
|
||||
if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this)
|
||||
InGameGroup = false;
|
||||
static int s_ColclButton = 0;
|
||||
if(m_pEditor->DoButton_Editor(&s_ColclButton, Localize("Clear collision"), InGameGroup?0:-1, &Button, 0, Localize("Removes collision from this layer")))
|
||||
|
@ -367,7 +395,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
|||
{0},
|
||||
};
|
||||
|
||||
if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this) // remove the image from the selection if this is the game/tele/speedup/front layer
|
||||
if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this) // remove the image from the selection if this is the game/tele/speedup/front/switch layer
|
||||
aProps[2].m_pName = 0;
|
||||
|
||||
static int s_aIds[NUM_PROPS] = {0};
|
||||
|
@ -684,8 +712,127 @@ void CLayerFront::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|||
continue;
|
||||
|
||||
// dont allow tele in and out tiles... same with speedup tile in front
|
||||
if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pFrontLayer && (l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_BOOST))
|
||||
if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pFrontLayer && (l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_BOOST || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_TRIGGER + ENTITY_OFFSET)|| l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_DOOR + ENTITY_OFFSET)))
|
||||
continue;
|
||||
m_pTiles[fy*m_Width+fx] = l->m_pTiles[y*l->m_Width+x];
|
||||
}
|
||||
}
|
||||
|
||||
CLayerSwitch::CLayerSwitch(int w, int h)
|
||||
: CLayerTiles(w, h)
|
||||
{
|
||||
m_pTypeName = "Switch";
|
||||
m_Switch = 1;
|
||||
|
||||
m_pSwitchTile = new CTeleTile[w*h];
|
||||
mem_zero(m_pSwitchTile, w*h*sizeof(CTeleTile));
|
||||
}
|
||||
|
||||
CLayerSwitch::~CLayerSwitch()
|
||||
{
|
||||
delete[] m_pSwitchTile;
|
||||
}
|
||||
|
||||
void CLayerSwitch::Resize(int NewW, int NewH)
|
||||
{
|
||||
// resize switch data
|
||||
CTeleTile *pNewSwitchData = new CTeleTile[NewW*NewH];
|
||||
mem_zero(pNewSwitchData, NewW*NewH*sizeof(CTeleTile));
|
||||
|
||||
// copy old data
|
||||
for(int y = 0; y < min(NewH, m_Height); y++)
|
||||
mem_copy(&pNewSwitchData[y*NewW], &m_pSwitchTile[y*m_Width], min(m_Width, NewW)*sizeof(CTeleTile));
|
||||
|
||||
// replace old
|
||||
delete [] m_pSwitchTile;
|
||||
m_pSwitchTile = pNewSwitchData;
|
||||
|
||||
// resize tile data
|
||||
CLayerTiles::Resize(NewW, NewH);
|
||||
|
||||
// resize gamelayer too
|
||||
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
||||
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
||||
}
|
||||
|
||||
void CLayerSwitch::BrushDraw(CLayer *pBrush, float wx, float wy)
|
||||
{
|
||||
CLayerSwitch *l = (CLayerSwitch *)pBrush;
|
||||
int sx = ConvertX(wx);
|
||||
int sy = ConvertY(wy);
|
||||
|
||||
for(int y = 0; y < l->m_Height; y++)
|
||||
for(int x = 0; x < l->m_Width; x++)
|
||||
{
|
||||
int fx = x+sx;
|
||||
int fy = y+sy;
|
||||
if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
||||
continue;
|
||||
|
||||
if(l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_DOOR + ENTITY_OFFSET) || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_TRIGGER + ENTITY_OFFSET) || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_LASER_LONG + ENTITY_OFFSET) || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_LASER_MIDDLE + ENTITY_OFFSET) || l->m_pTiles[y*l->m_Width+x].m_Index == (ENTITY_LASER_SHORT + ENTITY_OFFSET))
|
||||
{
|
||||
if(l->m_pSwitchTile[y*l->m_Width+x].m_Number)
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = l->m_pSwitchTile[y*l->m_Width+x].m_Number;
|
||||
else
|
||||
{
|
||||
if(!m_pEditor->m_SwitchNum)
|
||||
{
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = 0;
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Type = 0;
|
||||
m_pTiles[fy*m_Width+fx].m_Index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = m_pEditor->m_SwitchNum;
|
||||
}
|
||||
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index;
|
||||
m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = 0;
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Type = 0;
|
||||
m_pTiles[fy*m_Width+fx].m_Index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
||||
{
|
||||
if(m_Readonly)
|
||||
return;
|
||||
|
||||
int sx = ConvertX(Rect.x);
|
||||
int sy = ConvertY(Rect.y);
|
||||
int w = ConvertX(Rect.w);
|
||||
int h = ConvertY(Rect.h);
|
||||
|
||||
CLayerSwitch *pLt = static_cast<CLayerSwitch*>(pBrush);
|
||||
|
||||
for(int y = 0; y <= h; y++)
|
||||
{
|
||||
for(int x = 0; x <= w; x++)
|
||||
{
|
||||
int fx = x+sx;
|
||||
int fy = y+sy;
|
||||
|
||||
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
||||
continue;
|
||||
|
||||
if(Empty)
|
||||
{
|
||||
m_pTiles[fy*m_Width+fx].m_Index = 0;
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)];
|
||||
if(!pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number && m_pEditor->m_SwitchNum && m_pTiles[fy*m_Width+fx].m_Index > 0)
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = m_pEditor->m_SwitchNum;
|
||||
else
|
||||
m_pSwitchTile[fy*m_Width+fx].m_Number = pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,6 +144,23 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
|
|||
}
|
||||
}
|
||||
|
||||
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pSwitchLayer)
|
||||
{
|
||||
// new Switch layer
|
||||
View.HSplitBottom(10.0f, &View, &Button);
|
||||
View.HSplitBottom(12.0f, &View, &Button);
|
||||
static int s_NewSwitchLayerButton = 0;
|
||||
if(pEditor->DoButton_Editor(&s_NewSwitchLayerButton, "Add Switch Layer", 0, &Button, 0, "Creates a new switch layer"))
|
||||
{
|
||||
CLayer *l = new CLayerSwitch(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
|
||||
pEditor->m_Map.MakeSwitchLayer(l);
|
||||
pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
|
||||
pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
|
||||
pEditor->m_Brush.Clear();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// new tile layer
|
||||
View.HSplitBottom(10.0f, &View, &Button);
|
||||
|
@ -244,6 +261,8 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View)
|
|||
pEditor->m_Map.m_pTeleLayer = 0x0;
|
||||
if(pEditor->GetSelectedLayer(0) == pEditor->m_Map.m_pSpeedupLayer)
|
||||
pEditor->m_Map.m_pSpeedupLayer = 0x0;
|
||||
if(pEditor->GetSelectedLayer(0) == pEditor->m_Map.m_pSwitchLayer)
|
||||
pEditor->m_Map.m_pSwitchLayer = 0x0;
|
||||
pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->DeleteLayer(pEditor->m_SelectedLayer);
|
||||
return 1;
|
||||
}
|
||||
|
@ -268,7 +287,7 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View)
|
|||
{0},
|
||||
};
|
||||
|
||||
if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pTeleLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSpeedupLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pFrontLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer
|
||||
if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pTeleLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSpeedupLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pFrontLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSwitchLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer
|
||||
{
|
||||
aProps[0].m_Type = PROPTYPE_NULL;
|
||||
aProps[2].m_Type = PROPTYPE_NULL;
|
||||
|
@ -547,3 +566,29 @@ int CEditor::PopupSpeedup(CEditor *pEditor, CUIRect View)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEditor::PopupSwitch(CEditor *pEditor, CUIRect View)
|
||||
{
|
||||
CUIRect Button;
|
||||
View.HSplitBottom(12.0f, &View, &Button);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_Switch=0,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
CProperty aProps[] = {
|
||||
{"Number", pEditor->m_SwitchNum, PROPTYPE_INT_STEP, 0, 255},
|
||||
{0},
|
||||
};
|
||||
|
||||
static int s_aIds[NUM_PROPS] = {0};
|
||||
int NewVal = 0;
|
||||
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
|
||||
|
||||
if(Prop == PROP_Switch)
|
||||
pEditor->m_SwitchNum = clamp(NewVal, 0, 255);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ CLayers::CLayers()
|
|||
m_pTeleLayer = 0;
|
||||
m_pSpeedupLayer = 0;
|
||||
m_pFrontLayer = 0;
|
||||
m_pSwitchLayer = 0;
|
||||
m_pMap = 0;
|
||||
}
|
||||
|
||||
|
@ -54,6 +55,8 @@ void CLayers::Init(class IKernel *pKernel)
|
|||
m_pSpeedupLayer = pTilemap;
|
||||
if(pTilemap->m_Flags&8)
|
||||
m_pFrontLayer = pTilemap;
|
||||
if(pTilemap->m_Flags&16)
|
||||
m_pSwitchLayer = pTilemap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ class CLayers
|
|||
CMapItemLayerTilemap *m_pTeleLayer;
|
||||
CMapItemLayerTilemap *m_pSpeedupLayer;
|
||||
CMapItemLayerTilemap *m_pFrontLayer;
|
||||
CMapItemLayerTilemap *m_pSwitchLayer;
|
||||
class IMap *m_pMap;
|
||||
|
||||
public:
|
||||
|
@ -27,6 +28,7 @@ public:
|
|||
CMapItemLayerTilemap *TeleLayer() const { return m_pTeleLayer; }
|
||||
CMapItemLayerTilemap *SpeedupLayer() const { return m_pSpeedupLayer; }
|
||||
CMapItemLayerTilemap *FrontLayer() const { return m_pFrontLayer; }
|
||||
CMapItemLayerTilemap *SwitchLayer() const { return m_pSwitchLayer; }
|
||||
CMapItemGroup *GetGroup(int Index) const;
|
||||
CMapItemLayer *GetLayer(int Index) const;
|
||||
};
|
||||
|
|
|
@ -57,8 +57,9 @@ enum
|
|||
ENTITY_LASER_O_NORMAL,
|
||||
ENTITY_LASER_O_FAST,
|
||||
//DDRace - Plasma
|
||||
ENTITY_PLASMA=29,
|
||||
ENTITY_PLASMAE=29,
|
||||
ENTITY_PLASMAF,
|
||||
ENTITY_PLASMA,
|
||||
//DDRace - Shotgun
|
||||
ENTITY_CRAZY_SHOTGUN_U_EX=33,
|
||||
ENTITY_CRAZY_SHOTGUN_R_EX,
|
||||
|
@ -139,6 +140,16 @@ enum
|
|||
ENTITY_OFFSET=255-16*4,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
LAYER_GAME,
|
||||
LAYER_FRONT,
|
||||
LAYER_LASER,
|
||||
LAYER_TELE,
|
||||
LAYER_SPEEDUP,
|
||||
NUM_LAYERS
|
||||
};
|
||||
|
||||
struct CPoint
|
||||
{
|
||||
int x, y; // 22.10 fixed point
|
||||
|
@ -245,6 +256,7 @@ struct CMapItemLayerTilemap
|
|||
int m_Tele;
|
||||
int m_Speedup;
|
||||
int m_Front;
|
||||
int m_Switch;
|
||||
} ;
|
||||
|
||||
struct CMapItemLayerQuads
|
||||
|
|
|
@ -12,50 +12,52 @@ const int RANGE=700;
|
|||
//////////////////////////////////////////////////
|
||||
// CGun
|
||||
//////////////////////////////////////////////////
|
||||
CGun::CGun(CGameWorld *pGameWorld, vec2 m_Pos)
|
||||
CGun::CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive)
|
||||
: CEntity(pGameWorld, NETOBJTYPE_LASER)
|
||||
{
|
||||
DELAY=Server()->TickSpeed()*0.3f;
|
||||
this->m_Pos = m_Pos;
|
||||
this->eval_tick=Server()->Tick();
|
||||
m_Delay = Server()->TickSpeed()*0.3f;
|
||||
m_Pos = Pos;
|
||||
m_EvalTick = Server()->Tick();
|
||||
m_Freeze = Freeze;
|
||||
m_Explosive = Explosive;
|
||||
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
||||
|
||||
void CGun::fire()
|
||||
void CGun::Fire()
|
||||
{
|
||||
CCharacter *ents[16];
|
||||
int num = -1;
|
||||
num = GameServer()->m_World.FindEntities(m_Pos,RANGE, (CEntity**)ents, 16, NETOBJTYPE_CHARACTER);
|
||||
int id=-1;
|
||||
int minlen=0;
|
||||
for (int i = 0; i < num; i++)
|
||||
CCharacter *Ents[16];
|
||||
int Num = -1;
|
||||
Num = GameServer()->m_World.FindEntities(m_Pos,RANGE, (CEntity**)Ents, 16, NETOBJTYPE_CHARACTER);
|
||||
int Id=-1;
|
||||
int MinLen=0;
|
||||
for (int i = 0; i < Num; i++)
|
||||
{
|
||||
CCharacter *target = ents[i];
|
||||
CCharacter *Target = Ents[i];
|
||||
int res=0;
|
||||
vec2 coltile;
|
||||
res = GameServer()->Collision()->IntersectLine(m_Pos, target->m_Pos,0,0,false);
|
||||
res = GameServer()->Collision()->IntersectLine(m_Pos, Target->m_Pos,0,0,false);
|
||||
if (!res)
|
||||
{
|
||||
int len=length(ents[i]->m_Pos - m_Pos);
|
||||
if (minlen==0)
|
||||
int Len=length(Ents[i]->m_Pos - m_Pos);
|
||||
if (MinLen==0)
|
||||
{
|
||||
minlen=len;
|
||||
id=i;
|
||||
MinLen=Len;
|
||||
Id=i;
|
||||
}
|
||||
else if(minlen>len)
|
||||
else if(MinLen>Len)
|
||||
{
|
||||
minlen=len;
|
||||
id=i;
|
||||
MinLen=Len;
|
||||
Id=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id!=-1)
|
||||
if (Id!=-1)
|
||||
{
|
||||
CCharacter *target = ents[id];
|
||||
vec2 fdir = normalize(target->m_Pos - m_Pos);
|
||||
new CPlasma(&GameServer()->m_World, m_Pos,fdir);
|
||||
CCharacter *Target = Ents[Id];
|
||||
vec2 Fdir = normalize(Target->m_Pos - m_Pos);
|
||||
new CPlasma(&GameServer()->m_World, m_Pos, Fdir, m_Freeze, m_Explosive);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -69,14 +71,14 @@ void CGun::Tick()
|
|||
{
|
||||
if (Server()->Tick()%int(Server()->TickSpeed()*0.15f)==0)
|
||||
{
|
||||
eval_tick=Server()->Tick();
|
||||
int index = GameServer()->Collision()->IsCp(m_Pos.x,m_Pos.y);
|
||||
if (index)
|
||||
m_EvalTick=Server()->Tick();
|
||||
int Index = GameServer()->Collision()->IsCp(m_Pos.x,m_Pos.y);
|
||||
if (Index)
|
||||
{
|
||||
core=GameServer()->Collision()->CpSpeed(index);
|
||||
m_Core=GameServer()->Collision()->CpSpeed(Index);
|
||||
}
|
||||
m_Pos+=core;
|
||||
fire();
|
||||
m_Pos+=m_Core;
|
||||
Fire();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,5 +93,5 @@ void CGun::Snap(int snapping_client)
|
|||
pObj->m_Y = (int)m_Pos.y;
|
||||
pObj->m_FromX = (int)m_Pos.x;
|
||||
pObj->m_FromY = (int)m_Pos.y;
|
||||
pObj->m_StartTick = eval_tick;
|
||||
pObj->m_StartTick = m_EvalTick;
|
||||
}
|
||||
|
|
|
@ -10,15 +10,17 @@ class CCharacter;
|
|||
|
||||
class CGun : public CEntity
|
||||
{
|
||||
int eval_tick;
|
||||
int m_EvalTick;
|
||||
|
||||
vec2 core;
|
||||
vec2 m_Core;
|
||||
bool m_Freeze;
|
||||
bool m_Explosive;
|
||||
|
||||
void fire();
|
||||
int DELAY;
|
||||
void Fire();
|
||||
int m_Delay;
|
||||
|
||||
public:
|
||||
CGun(CGameWorld *pGameWorld, vec2 pos);
|
||||
CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive);
|
||||
|
||||
virtual void Reset();
|
||||
virtual void Tick();
|
||||
|
|
|
@ -32,11 +32,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
|
|||
m_Energy = -1;
|
||||
if ((m_Type == 1 && g_Config.m_SvHit))
|
||||
{
|
||||
if(g_Config.m_SvOldShotgun)
|
||||
Hit->m_Core.m_Vel+=normalize(OwnerChar->m_Core.m_Pos-Hit->m_Core.m_Pos)*10;
|
||||
else
|
||||
Hit->m_Core.m_Vel+=normalize(m_PrevPos - Hit->m_Core.m_Pos) * 10;
|
||||
} else if (m_Type == 0)
|
||||
}
|
||||
else if (m_Type == 0)
|
||||
{
|
||||
Hit->UnFreeze();
|
||||
}
|
||||
|
|
|
@ -11,31 +11,36 @@ const float ACCEL=1.1f;
|
|||
//////////////////////////////////////////////////
|
||||
// turret
|
||||
//////////////////////////////////////////////////
|
||||
CPlasma::CPlasma(CGameWorld *pGameWorld, vec2 pos,vec2 dir)
|
||||
CPlasma::CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze, bool Explosive)
|
||||
: CEntity(pGameWorld, NETOBJTYPE_LASER)
|
||||
{
|
||||
this->m_Pos = pos;
|
||||
this->core = dir;
|
||||
this->eval_tick=Server()->Tick();
|
||||
this->lifetime=Server()->TickSpeed()*1.5;
|
||||
m_Pos = Pos;
|
||||
m_Core = Dir;
|
||||
m_Freeze = Freeze;
|
||||
m_Explosive = Explosive;
|
||||
m_EvalTick = Server()->Tick();
|
||||
m_LifeTime = Server()->TickSpeed() * 1.5;
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
||||
bool CPlasma::hit_character()
|
||||
bool CPlasma::HitCharacter()
|
||||
{
|
||||
vec2 to2;
|
||||
CCharacter *hit = GameServer()->m_World.IntersectCharacter(m_Pos, m_Pos+core, 0.0f,to2);
|
||||
if(!hit)
|
||||
vec2 To2;
|
||||
CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos, m_Pos+m_Core, 0.0f,To2);
|
||||
if(!Hit)
|
||||
return false;
|
||||
hit->Freeze(Server()->TickSpeed()*3);
|
||||
if(m_Freeze)
|
||||
Hit->Freeze(Server()->TickSpeed()*3);
|
||||
else
|
||||
GameServer()->CreateExplosion(m_Pos, -1, WEAPON_GRENADE, false);
|
||||
GameServer()->m_World.DestroyEntity(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlasma::move()
|
||||
void CPlasma::Move()
|
||||
{
|
||||
m_Pos += core;
|
||||
core *= ACCEL;
|
||||
m_Pos += m_Core;
|
||||
m_Core *= ACCEL;
|
||||
}
|
||||
|
||||
void CPlasma::Reset()
|
||||
|
@ -45,20 +50,21 @@ void CPlasma::Reset()
|
|||
|
||||
void CPlasma::Tick()
|
||||
{
|
||||
if (lifetime==0)
|
||||
if (m_LifeTime==0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
lifetime--;
|
||||
move();
|
||||
hit_character();
|
||||
m_LifeTime--;
|
||||
Move();
|
||||
HitCharacter();
|
||||
|
||||
int res=0;
|
||||
res = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos+core,0, 0);
|
||||
if(res)
|
||||
int Res=0;
|
||||
Res = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos+m_Core,0, 0);
|
||||
if(Res)
|
||||
{
|
||||
GameServer()->CreateExplosion(m_Pos, -1, WEAPON_GRENADE, false);
|
||||
if(m_Explosive)
|
||||
GameServer()->CreateExplosion(m_Pos, -1, WEAPON_GRENADE, false);
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
@ -74,5 +80,5 @@ void CPlasma::Snap(int snapping_client)
|
|||
pObj->m_Y = (int)m_Pos.y;
|
||||
pObj->m_FromX = (int)m_Pos.x;
|
||||
pObj->m_FromY = (int)m_Pos.y;
|
||||
pObj->m_StartTick = eval_tick;
|
||||
pObj->m_StartTick = m_EvalTick;
|
||||
}
|
||||
|
|
|
@ -9,14 +9,15 @@ class CGun;
|
|||
|
||||
class CPlasma : public CEntity
|
||||
{
|
||||
vec2 core;
|
||||
int eval_tick;
|
||||
int lifetime;
|
||||
|
||||
bool hit_character();
|
||||
void move();
|
||||
vec2 m_Core;
|
||||
int m_EvalTick;
|
||||
int m_LifeTime;
|
||||
bool m_Freeze;
|
||||
bool m_Explosive;
|
||||
bool HitCharacter();
|
||||
void Move();
|
||||
public:
|
||||
CPlasma(CGameWorld *pGameWorld, vec2 pos,vec2 dir);
|
||||
CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze, bool Explosive);
|
||||
|
||||
virtual void Reset();
|
||||
virtual void Tick();
|
||||
|
@ -24,4 +25,4 @@ public:
|
|||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1920,7 +1920,7 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
|
|||
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
||||
m_World.SetGameServer(this);
|
||||
m_Events.SetGameServer(this);
|
||||
|
||||
m_Size = 0;
|
||||
//if(!data) // only load once
|
||||
//data = load_data_from_memory(internal_data);
|
||||
|
||||
|
@ -1974,12 +1974,67 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
|
|||
CTile *pFront=0;
|
||||
if (m_Layers.FrontLayer())
|
||||
pFront = (CTile *)Kernel()->RequestInterface<IMap>()->GetData(pTileMap->m_Front);
|
||||
CTeleTile *pSwitch=0;
|
||||
if (m_Layers.SwitchLayer())
|
||||
pSwitch = (CTeleTile *)Kernel()->RequestInterface<IMap>()->GetData(pTileMap->m_Switch);
|
||||
if (pSwitch)
|
||||
{
|
||||
if(Collision()->Layers()->SwitchLayer())
|
||||
for(int i = 0; i < Collision()->Layers()->SwitchLayer()->m_Width * Collision()->Layers()->SwitchLayer()->m_Height; i++)
|
||||
if(Collision()->SwitchLayer()[i].m_Type == (ENTITY_DOOR + ENTITY_OFFSET))
|
||||
m_Size++;
|
||||
if(m_Size)
|
||||
{
|
||||
m_SDoors = new SDoors[m_Size];
|
||||
int num=0;
|
||||
for(int y = 0; y < pTileMap->m_Height; y++)
|
||||
for(int x = 0; x < pTileMap->m_Width; x++)
|
||||
if(Collision()->SwitchLayer()[y*pTileMap->m_Width+x].m_Type == (ENTITY_DOOR + ENTITY_OFFSET))
|
||||
{
|
||||
int sides[8][2];
|
||||
sides[0][0]=pSwitch[(x)+pTileMap->m_Width*(y+1)].m_Type - ENTITY_OFFSET;
|
||||
sides[1][0]=pSwitch[(x+1)+pTileMap->m_Width*(y+1)].m_Type - ENTITY_OFFSET;
|
||||
sides[2][0]=pSwitch[(x+1)+pTileMap->m_Width*(y)].m_Type - ENTITY_OFFSET;
|
||||
sides[3][0]=pSwitch[(x+1)+pTileMap->m_Width*(y-1)].m_Type - ENTITY_OFFSET;
|
||||
sides[4][0]=pSwitch[(x)+pTileMap->m_Width*(y-1)].m_Type - ENTITY_OFFSET;
|
||||
sides[5][0]=pSwitch[(x-1)+pTileMap->m_Width*(y-1)].m_Type - ENTITY_OFFSET;
|
||||
sides[6][0]=pSwitch[(x-1)+pTileMap->m_Width*(y)].m_Type - ENTITY_OFFSET;
|
||||
sides[7][0]=pSwitch[(x-1)+pTileMap->m_Width*(y+1)].m_Type - ENTITY_OFFSET;
|
||||
sides[0][1]=pSwitch[(x)+pTileMap->m_Width*(y+1)].m_Number;
|
||||
sides[1][1]=pSwitch[(x+1)+pTileMap->m_Width*(y+1)].m_Number;
|
||||
sides[2][1]=pSwitch[(x+1)+pTileMap->m_Width*(y)].m_Number;
|
||||
sides[3][1]=pSwitch[(x+1)+pTileMap->m_Width*(y-1)].m_Number;
|
||||
sides[4][1]=pSwitch[(x)+pTileMap->m_Width*(y-1)].m_Number;
|
||||
sides[5][1]=pSwitch[(x-1)+pTileMap->m_Width*(y-1)].m_Number;
|
||||
sides[6][1]=pSwitch[(x-1)+pTileMap->m_Width*(y)].m_Number;
|
||||
sides[7][1]=pSwitch[(x-1)+pTileMap->m_Width*(y+1)].m_Number;
|
||||
for(int i=0; i<8;i++)
|
||||
if ((sides[i][0] >= ENTITY_LASER_SHORT && sides[i][0] <= ENTITY_LASER_LONG) && Collision()->SwitchLayer()[y*pTileMap->m_Width+x].m_Number == sides[i][1])
|
||||
{
|
||||
vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f);
|
||||
m_SDoors[num].m_Address = new CDoor(&m_World, Pos, pi/4*i, (32*3 + 32*(sides[i][0] - ENTITY_LASER_SHORT)*3), false);
|
||||
m_SDoors[num].m_Pos = Pos;
|
||||
m_SDoors[num++].m_Number = Collision()->SwitchLayer()[y*pTileMap->m_Width+x].m_Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int y = 0; y < pTileMap->m_Height; y++)
|
||||
{
|
||||
for(int x = 0; x < pTileMap->m_Width; x++)
|
||||
{
|
||||
int Index = pTiles[y*pTileMap->m_Width+x].m_Index;
|
||||
if(Index == TILE_NPC)
|
||||
g_Config.m_SvNpc = 1;
|
||||
else if (Index == TILE_EHOOK)
|
||||
g_Config.m_SvEndlessDrag = 1;
|
||||
else if (Index == TILE_NOHIT)
|
||||
g_Config.m_SvHit = 0;
|
||||
else if (Index == TILE_EHOOK)
|
||||
g_Config.m_SvEndlessDrag = 1;
|
||||
else if (Index == TILE_NPH)
|
||||
g_Config.m_SvPhook = 0;
|
||||
if(Index >= ENTITY_OFFSET)
|
||||
{
|
||||
vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f);
|
||||
|
@ -1987,13 +2042,33 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
|
|||
}
|
||||
if (pFront)
|
||||
{
|
||||
int FIndex = pFront[y*pTileMap->m_Width+x].m_Index;
|
||||
if(FIndex >= ENTITY_OFFSET)
|
||||
int Index = pFront[y*pTileMap->m_Width+x].m_Index;
|
||||
if(Index == TILE_NPC)
|
||||
g_Config.m_SvNpc = 1;
|
||||
else if (Index == TILE_EHOOK)
|
||||
g_Config.m_SvEndlessDrag = 1;
|
||||
else if (Index == TILE_NOHIT)
|
||||
g_Config.m_SvHit = 0;
|
||||
else if (Index == TILE_EHOOK)
|
||||
g_Config.m_SvEndlessDrag = 1;
|
||||
else if (Index == TILE_NPH)
|
||||
g_Config.m_SvPhook = 0;
|
||||
|
||||
if(Index >= ENTITY_OFFSET)
|
||||
{
|
||||
vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f);
|
||||
m_pController->OnEntity(FIndex-ENTITY_OFFSET, Pos,true);
|
||||
m_pController->OnEntity(Index-ENTITY_OFFSET, Pos,true);
|
||||
}
|
||||
}
|
||||
if (pSwitch)
|
||||
{
|
||||
int Index = pSwitch[y*pTileMap->m_Width+x].m_Type - ENTITY_OFFSET;
|
||||
vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f);
|
||||
if(Index == ENTITY_TRIGGER)
|
||||
for(int i=0;i<m_Size;i++)
|
||||
if(m_SDoors[i].m_Number == pSwitch[y*pTileMap->m_Width+x].m_Number)
|
||||
new CTrigger(&m_World,Pos, m_SDoors[i].m_Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "gameworld.h"
|
||||
#include "player.h"
|
||||
#include "score.h"
|
||||
#include <game/server/entities/door.h>
|
||||
#include <game/server/entities/trigger.h>
|
||||
|
||||
/*
|
||||
Tick
|
||||
|
@ -123,15 +125,20 @@ public:
|
|||
|
||||
CEventHandler m_Events;
|
||||
CPlayer *m_apPlayers[MAX_CLIENTS];
|
||||
|
||||
//bool m_Cheats;
|
||||
//bool m_EndlessDrag;
|
||||
//bool m_Tunes;
|
||||
//bool m_PlayersCollision;
|
||||
|
||||
IGameController *m_pController;
|
||||
CGameWorld m_World;
|
||||
int m_Size;
|
||||
|
||||
struct SDoors
|
||||
{
|
||||
int m_Number;
|
||||
vec2 m_Pos;
|
||||
CDoor * m_Address;
|
||||
};
|
||||
|
||||
SDoors* m_SDoors;
|
||||
|
||||
// helper functions
|
||||
class CCharacter *GetPlayerChar(int ClientId);
|
||||
|
||||
|
|
|
@ -156,19 +156,6 @@ bool IGameController::OnEntity(int Index, vec2 Pos, bool Front)
|
|||
else if(Index == ENTITY_SPAWN_BLUE)
|
||||
m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos;
|
||||
|
||||
if(Index == ENTITY_DOOR)
|
||||
{
|
||||
for(int i=0; i<8;i++)
|
||||
{
|
||||
if (sides[i] >= ENTITY_LASER_SHORT && sides[i] <= ENTITY_LASER_LONG)
|
||||
{
|
||||
CDoor * door = new CDoor(&GameServer()->m_World, Pos, pi/4*i, 32*3 + 32*(sides[i] - ENTITY_LASER_SHORT)*3, false);
|
||||
//for (int j = 0; j < 8; j++)
|
||||
// if (j != i)
|
||||
Connector(vec2(x, y), door, Front);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Index >= ENTITY_CRAZY_SHOTGUN_U_EX && Index <= ENTITY_CRAZY_SHOTGUN_L_EX)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
@ -305,9 +292,17 @@ bool IGameController::OnEntity(int Index, vec2 Pos, bool Front)
|
|||
{
|
||||
new CDragger(&GameServer()->m_World, Pos,Index-ENTITY_DRAGGER_WEAK_NW+1,true);
|
||||
}
|
||||
else if(Index==ENTITY_PLASMAE)
|
||||
{
|
||||
new CGun(&GameServer()->m_World, Pos, false, true);
|
||||
}
|
||||
else if(Index==ENTITY_PLASMAF)
|
||||
{
|
||||
new CGun(&GameServer()->m_World, Pos, true, false);
|
||||
}
|
||||
else if(Index==ENTITY_PLASMA)
|
||||
{
|
||||
new CGun(&GameServer()->m_World, Pos);
|
||||
new CGun(&GameServer()->m_World, Pos, true, true);
|
||||
}
|
||||
if(Type != -1)
|
||||
{
|
||||
|
@ -335,29 +330,6 @@ vec2 GetSidePos(int side) {
|
|||
return vec2(0, 0);
|
||||
}
|
||||
|
||||
void IGameController::Connector(vec2 Pos, CDoor* Door, bool Front) {
|
||||
int sides[8];
|
||||
sides[0] = GameServer()->Collision()->Entity(Pos.x, Pos.y + 1, Front);
|
||||
sides[1] = GameServer()->Collision()->Entity(Pos.x + 1, Pos.y + 1, Front);
|
||||
sides[2] = GameServer()->Collision()->Entity(Pos.x + 1, Pos.y, Front);
|
||||
sides[3] = GameServer()->Collision()->Entity(Pos.x + 1, Pos.y - 1, Front);
|
||||
sides[4] = GameServer()->Collision()->Entity(Pos.x, Pos.y - 1, Front);
|
||||
sides[5] = GameServer()->Collision()->Entity(Pos.x - 1, Pos.y - 1, Front);
|
||||
sides[6] = GameServer()->Collision()->Entity(Pos.x - 1, Pos.y, Front);
|
||||
sides[7] = GameServer()->Collision()->Entity(Pos.x - 1, Pos.y + 1, Front);
|
||||
for (int i=0;i<8;i++)
|
||||
{
|
||||
vec2 shift = GetSidePos(i);
|
||||
if (sides[i]==ENTITY_CONNECTOR_D+(i+4) % 8)
|
||||
Connector(Pos + shift, Door, Front);
|
||||
else if(sides[i]==ENTITY_TRIGGER)
|
||||
{
|
||||
vec2 pos((Pos.x + shift.x)*32.0f + 16.0f, (Pos.y + shift.y)*32.0f + 16.0f);
|
||||
new CTrigger(&GameServer()->m_World,pos, Door);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IGameController::EndRound()
|
||||
{
|
||||
if(m_Warmup) // game can't end when we are running warmup
|
||||
|
|
|
@ -108,7 +108,6 @@ public:
|
|||
*/
|
||||
virtual bool OnEntity(int Index, vec2 Pos, bool Front);
|
||||
|
||||
virtual void Connector(vec2 Pos, CDoor* Door, bool Front);
|
||||
|
||||
/*
|
||||
Function: on_CCharacter_spawn
|
||||
|
|
|
@ -31,8 +31,8 @@ void CGameControllerDDRace::InitTeleporter()
|
|||
for(int i = 0; i < GameServer()->Collision()->Layers()->TeleLayer()->m_Width*GameServer()->Collision()->Layers()->TeleLayer()->m_Height; i++)
|
||||
{
|
||||
// get the array size
|
||||
if(GameServer()->Collision()->m_pTele[i].m_Number > ArraySize)
|
||||
ArraySize = GameServer()->Collision()->m_pTele[i].m_Number;
|
||||
if(GameServer()->Collision()->TeleLayer()[i].m_Number > ArraySize)
|
||||
ArraySize = GameServer()->Collision()->TeleLayer()[i].m_Number;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ void CGameControllerDDRace::InitTeleporter()
|
|||
// assign the values
|
||||
for(int i = 0; i < GameServer()->Collision()->Layers()->TeleLayer()->m_Width*GameServer()->Collision()->Layers()->TeleLayer()->m_Height; i++)
|
||||
{
|
||||
if(GameServer()->Collision()->m_pTele[i].m_Number > 0 && GameServer()->Collision()->m_pTele[i].m_Type == TILE_TELEOUT)
|
||||
m_pTeleporter[GameServer()->Collision()->m_pTele[i].m_Number-1] = vec2(i%GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16, i/GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16);
|
||||
if(GameServer()->Collision()->TeleLayer()[i].m_Number > 0 && GameServer()->Collision()->TeleLayer()[i].m_Type == TILE_TELEOUT)
|
||||
m_pTeleporter[GameServer()->Collision()->TeleLayer()[i].m_Number-1] = vec2(i%GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16, i/GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue