Browse Source

Merge branch 'master' of github.com:diasurgical/devilution

pull/888/head
Anders Jenbo 6 years ago
parent
commit
09a51aa300
  1. 1
      Source/gamemenu.h
  2. 97
      Source/lighting.cpp
  3. 19
      Source/mainmenu.cpp
  4. 2
      Source/objdat.cpp
  5. 7
      Source/objects.cpp
  6. 101
      Source/towners.cpp
  7. 2
      SourceX/DiabloUI/mainmenu.cpp
  8. 13
      enums.h

1
Source/gamemenu.h

@ -13,6 +13,7 @@ extern "C" {
#endif
#ifdef HELLFIRE
extern char *jogging_title;
extern BOOL jogging_opt;
#endif

97
Source/lighting.cpp

@ -558,11 +558,19 @@ void DoLighting(int nXPos, int nYPos, int nRadius, int Lnum)
max_y = 15;
}
#ifdef HELLFIRE
if (currlevel < 17) {
#else
if (nXPos >= 0 && nXPos < MAXDUNX && nYPos >= 0 && nYPos < MAXDUNY) {
#endif
dLight[nXPos][nYPos] = 0;
#ifdef HELLFIRE
} else if (dLight[nXPos][nYPos] > lightradius[nRadius][0]) {
dLight[nXPos][nYPos] = lightradius[nRadius][0];
#endif
}
mult = xoff + 8 * yoff;
mult = xoff + 8*yoff;
for (y = 0; y < min_y; y++) {
for (x = 1; x < max_x; x++) {
radius_block = lightblock[mult][y][x];
@ -570,11 +578,11 @@ void DoLighting(int nXPos, int nYPos, int nRadius, int Lnum)
temp_x = nXPos + x;
temp_y = nYPos + y;
v = lightradius[nRadius][radius_block];
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY) {
if (v < dLight[temp_x][temp_y]) {
#ifndef HELLFIRE
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY)
#endif
if (v < dLight[temp_x][temp_y])
dLight[temp_x][temp_y] = v;
}
}
}
}
}
@ -587,11 +595,11 @@ void DoLighting(int nXPos, int nYPos, int nRadius, int Lnum)
temp_x = nXPos + y;
temp_y = nYPos - x;
v = lightradius[nRadius][radius_block];
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY) {
if (v < dLight[temp_x][temp_y]) {
#ifndef HELLFIRE
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY)
#endif
if (v < dLight[temp_x][temp_y])
dLight[temp_x][temp_y] = v;
}
}
}
}
}
@ -604,11 +612,11 @@ void DoLighting(int nXPos, int nYPos, int nRadius, int Lnum)
temp_x = nXPos - x;
temp_y = nYPos - y;
v = lightradius[nRadius][radius_block];
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY) {
if (v < dLight[temp_x][temp_y]) {
#ifndef HELLFIRE
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY)
#endif
if (v < dLight[temp_x][temp_y])
dLight[temp_x][temp_y] = v;
}
}
}
}
}
@ -621,11 +629,11 @@ void DoLighting(int nXPos, int nYPos, int nRadius, int Lnum)
temp_x = nXPos - y;
temp_y = nYPos + x;
v = lightradius[nRadius][radius_block];
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY) {
if (v < dLight[temp_x][temp_y]) {
#ifndef HELLFIRE
if (temp_x >= 0 && temp_x < MAXDUNX && temp_y >= 0 && temp_y < MAXDUNY)
#endif
if (v < dLight[temp_x][temp_y])
dLight[temp_x][temp_y] = v;
}
}
}
}
}
@ -911,6 +919,21 @@ void MakeLightTable()
}
tbl += 224;
}
#ifdef HELLFIRE
if (currlevel >= 17) {
tbl = pLightTbl;
for (i = 0; i < lights; i++) {
*tbl++ = 0;
for (j = 1; j < 16; j++)
*tbl++ = j;
tbl += 240;
}
*tbl++ = 0;
for (j = 1; j < 16; j++)
*tbl++ = 1;
tbl += 240;
}
#endif
trn = LoadFileInMem("PlrGFX\\Infra.TRN", NULL);
for (i = 0; i < 256; i++) {
@ -950,27 +973,41 @@ void MakeLightTable()
*tbl++ = 0;
}
for (k = 0; k < 16; k++) {
for (l = 0; l < 128; l++) {
if (l > (k + 1) * 8) {
lightradius[k][l] = 15;
for (j = 0; j < 16; j++) {
for (i = 0; i < 128; i++) {
if (i > (j + 1) * 8) {
lightradius[j][i] = 15;
} else {
lightradius[k][l] = l * 15.0 / ((k + 1) * 8.0) + 0.5;
fs = (double)15 * i / ((double)8 * (j + 1));
lightradius[j][i] = (BYTE)(fs + 0.5);
}
}
}
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
#ifdef HELLFIRE
if (currlevel >= 17) {
for (j = 0; j < 16; j++) {
fa = (sqrt((double)(16 - j))) / 128;
fa *= fa;
for (i = 0; i < 128; i++) {
lightradius[15 - j][i] = 15 - (BYTE)(fa * (double)((128 - i) * (128 - i)));
if (lightradius[15 - j][i] > 15)
lightradius[15 - j][i] = 0;
lightradius[15 - j][i] = lightradius[15 - j][i] - (BYTE)((15 - j) / 2);
if (lightradius[15 - j][i] > 15)
lightradius[15 - j][i] = 0;
}
}
}
#endif
for (j = 0; j < 8; j++) {
for (i = 0; i < 8; i++) {
for (k = 0; k < 16; k++) {
for (l = 0; l < 16; l++) {
fs = (BYTE)sqrt((double)(8 * l - j) * (8 * l - j) + (8 * k - i) * (8 * k - i));
if (fs < 0.0) {
fa = -0.5;
} else {
fa = 0.5;
}
lightblock[i * 8 + j][k][l] = fs + fa;
fs += fs < 0 ? -0.5 : 0.5;
lightblock[j * 8 + i][k][l] = fs;
}
}
}

19
Source/mainmenu.cpp

@ -96,8 +96,8 @@ void mainmenu_loop()
BOOL done;
int menu;
done = FALSE;
mainmenu_refresh_music();
done = FALSE;
do {
menu = 0;
@ -113,23 +113,23 @@ void mainmenu_loop()
if (!mainmenu_multi_player())
done = TRUE;
break;
case MAINMENU_REPLAY_INTRO:
case MAINMENU_ATTRACT_MODE:
#ifdef HELLFIRE
if (gbActive)
mainmenu_play_intro();
#else
case MAINMENU_REPLAY_INTRO:
#ifdef SPAWN
done = FALSE;
#else
if (gbActive)
mainmenu_play_intro();
#endif
#endif
break;
case MAINMENU_SHOW_CREDITS:
UiCreditsDialog(16);
break;
#ifdef HELLFIRE
case MAINMENU_SHOW_SUPPORT:
//UiSupportDialog(16);
break;
#endif
case MAINMENU_EXIT_DIABLO:
done = TRUE;
break;
@ -141,6 +141,11 @@ void mainmenu_loop()
BOOL mainmenu_single_player()
{
#ifdef HELLFIRE
if (!SRegLoadValue(APP_NAME, jogging_title, 0, &jogging_opt)) {
jogging_opt = TRUE;
}
#endif
gbMaxPlayers = 1;
if (!SRegLoadValue("devilutionx", "game speed", 0, &ticks_per_sec)) {

2
Source/objdat.cpp

@ -242,7 +242,7 @@ ObjDataStruct AllObjects[] = {
{ 1, OFILE_MCIRL, 0, 0, DTYPE_CATHEDRAL, THEME_NONE, Q_BETRAYER, 0, 1, 0, 96, FALSE, TRUE, TRUE, 0, 0, FALSE },
{ 1, OFILE_MCIRL, 0, 0, DTYPE_CATHEDRAL, THEME_NONE, Q_BETRAYER, 0, 1, 0, 96, FALSE, TRUE, TRUE, 0, 0, FALSE },
#ifdef HELLFIRE
{ 1, OFILE_BKSLBRNT, 1, 24, 0, THEME_NONE, -1, 0, 1, 0, 96, TRUE, TRUE, TRUE, 0, 3, FALSE }, // BUGFIX should only be loaded on level 4-12 like in the original
{ 1, OFILE_BKSLBRNT, 1, 24, 0, THEME_NONE, -1, 0, 1, 0, 96, TRUE, TRUE, TRUE, 0, 3, FALSE }, // BUGFIX should only be loaded on level 1-12 (crypt masks as 1-4)
#else
{ 1, OFILE_BKSLBRNT, 4, 12, 0, THEME_NONE, -1, 0, 1, 0, 96, TRUE, TRUE, TRUE, 0, 3, FALSE },
#endif

7
Source/objects.cpp

@ -5248,6 +5248,13 @@ void GetObjectStr(int i)
break;
case OBJ_BARREL:
case OBJ_BARRELEX:
#ifdef HELLFIRE
if (currlevel > 16 && currlevel < 21) // for hive levels
strcpy(infostr, "Pod"); //Then a barrel is called a pod
else if (currlevel > 20 && currlevel < 25) // for crypt levels
strcpy(infostr, "Urn"); //Then a barrel is called an urn
else
#endif
strcpy(infostr, "Barrel");
break;
case OBJ_SKELBOOK:

101
Source/towners.cpp

@ -773,6 +773,9 @@ void TalkToTowner(int p, int t)
towner[t]._tMsgSaid = TRUE;
}
if ((plr[p]._pLvlVisited[2] || plr[p]._pLvlVisited[4]) && quests[Q_SKELKING]._qactive != QUEST_NOTAVAIL) {
#ifdef HELLFIRE
if (quests[Q_SKELKING]._qactive != QUEST_NOTAVAIL)
#endif
if (quests[Q_SKELKING]._qvar2 == 0 && !towner[t]._tMsgSaid) {
quests[Q_SKELKING]._qvar2 = 1;
quests[Q_SKELKING]._qlog = TRUE;
@ -796,7 +799,8 @@ void TalkToTowner(int p, int t)
NetSendCmdQuest(TRUE, Q_SKELKING);
}
}
if (gbMaxPlayers == 1 && plr[p]._pLvlVisited[3] && quests[Q_LTBANNER]._qactive != QUEST_NOTAVAIL) {
if (gbMaxPlayers == 1) {
if (plr[p]._pLvlVisited[3] && quests[Q_LTBANNER]._qactive != QUEST_NOTAVAIL) {
if ((quests[Q_LTBANNER]._qactive == QUEST_INIT || quests[Q_LTBANNER]._qactive == QUEST_ACTIVE) && quests[Q_LTBANNER]._qvar2 == 0 && !towner[t]._tMsgSaid) {
quests[Q_LTBANNER]._qvar2 = 1;
if (quests[Q_LTBANNER]._qactive == QUEST_INIT) {
@ -809,9 +813,11 @@ void TalkToTowner(int p, int t)
InitQTextMsg(TEXT_BANNER2);
towner[t]._tMsgSaid = TRUE;
}
if (quests[Q_LTBANNER]._qvar2 == 1 && PlrHasItem(p, IDI_BANNER, &i) != NULL) {
#ifndef HELLFIRE
if (!towner[t]._tMsgSaid) {
#ifdef HELLFIRE
}
if (!towner[t]._tMsgSaid && PlrHasItem(p, IDI_BANNER, &i) != NULL) {
#else
if (quests[Q_LTBANNER]._qvar2 == 1 && PlrHasItem(p, IDI_BANNER, &i) != NULL && !towner[t]._tMsgSaid) {
#endif
quests[Q_LTBANNER]._qactive = QUEST_DONE;
quests[Q_LTBANNER]._qvar1 = 3;
@ -821,11 +827,11 @@ void TalkToTowner(int p, int t)
towner[t]._tVar1 = p;
InitQTextMsg(TEXT_BANNER3);
towner[t]._tMsgSaid = TRUE;
}
}
#ifndef HELLFIRE
}
#endif
}
}
if (!qtextflag) {
TownerTalk(TEXT_OGDEN1, t);
if (storeflag) {
@ -875,6 +881,9 @@ void TalkToTowner(int p, int t)
} else if (t == GetActiveTowner(TOWN_SMITH)) {
if (gbMaxPlayers == 1) {
if (plr[p]._pLvlVisited[4] && quests[Q_ROCK]._qactive != QUEST_NOTAVAIL) {
#ifdef HELLFIRE
if (quests[Q_ROCK]._qactive != QUEST_NOTAVAIL)
#endif
if (quests[Q_ROCK]._qvar2 == 0) {
quests[Q_ROCK]._qvar2 = 1;
quests[Q_ROCK]._qlog = TRUE;
@ -887,9 +896,11 @@ void TalkToTowner(int p, int t)
InitQTextMsg(TEXT_INFRA5);
towner[t]._tMsgSaid = TRUE;
}
if (quests[Q_ROCK]._qvar2 == 1 && PlrHasItem(p, IDI_ROCK, &i) != NULL) {
#ifndef HELLFIRE
if (!towner[t]._tMsgSaid) {
#ifdef HELLFIRE
}
if (!towner[t]._tMsgSaid && PlrHasItem(p, IDI_ROCK, &i) != NULL) {
#else
if (quests[Q_ROCK]._qvar2 == 1 && PlrHasItem(p, IDI_ROCK, &i) != NULL && !towner[t]._tMsgSaid) {
#endif
quests[Q_ROCK]._qactive = QUEST_DONE;
quests[Q_ROCK]._qvar2 = 2;
@ -900,11 +911,10 @@ void TalkToTowner(int p, int t)
towner[t]._tVar1 = p;
InitQTextMsg(TEXT_INFRA7);
towner[t]._tMsgSaid = TRUE;
#ifndef HELLFIRE
}
#endif
#ifndef HELLFIRE
}
}
#endif
if (plr[p]._pLvlVisited[9] && quests[Q_ANVIL]._qactive != QUEST_NOTAVAIL) {
if ((quests[Q_ANVIL]._qactive == QUEST_INIT || quests[Q_ANVIL]._qactive == QUEST_ACTIVE) && quests[Q_ANVIL]._qvar2 == 0 && !towner[t]._tMsgSaid) {
if (quests[Q_ROCK]._qvar2 == 2 || quests[Q_ROCK]._qactive == QUEST_ACTIVE && quests[Q_ROCK]._qvar2 == 1) {
@ -920,8 +930,11 @@ void TalkToTowner(int p, int t)
towner[t]._tMsgSaid = TRUE;
}
}
#ifdef HELLFIRE
}
if (!towner[t]._tMsgSaid && PlrHasItem(p, IDI_ANVIL, &i) != NULL) {
#else
if (quests[Q_ANVIL]._qvar2 == 1 && PlrHasItem(p, IDI_ANVIL, &i) != NULL) {
#ifndef HELLFIRE
if (!towner[t]._tMsgSaid) {
#endif
quests[Q_ANVIL]._qactive = QUEST_DONE;
@ -935,8 +948,8 @@ void TalkToTowner(int p, int t)
towner[t]._tMsgSaid = TRUE;
#ifndef HELLFIRE
}
#endif
}
#endif
}
}
if (!qtextflag) {
@ -1004,7 +1017,7 @@ void TalkToTowner(int p, int t)
quests[Q_GRAVE]._qactive = 2;
quests[Q_GRAVE]._qlog = 1;
quests[Q_GRAVE]._qmsg = TEXT_GRAVE8;
InitQTextMsg(Q_GRAVE);
InitQTextMsg(TEXT_GRAVE8);
towner[t]._tMsgSaid = TRUE;
}
#endif
@ -1024,10 +1037,11 @@ void TalkToTowner(int p, int t)
} else if (t == GetActiveTowner(TOWN_HEALER)) {
if (gbMaxPlayers == 1) {
#ifdef HELLFIRE
if ((plr[p]._pLvlVisited[1] || plr[p]._pLvlVisited[5]) && !towner[t]._tMsgSaid) {
if (plr[p]._pLvlVisited[1] || plr[p]._pLvlVisited[5]) {
#else
if (plr[p]._pLvlVisited[1] && !towner[t]._tMsgSaid) {
if (plr[p]._pLvlVisited[1]) {
#endif
if (!towner[t]._tMsgSaid) {
if (quests[Q_PWATER]._qactive == QUEST_INIT) {
quests[Q_PWATER]._qactive = QUEST_ACTIVE;
quests[Q_PWATER]._qlog = TRUE;
@ -1046,6 +1060,7 @@ void TalkToTowner(int p, int t)
towner[t]._tMsgSaid = TRUE;
}
}
}
if (quests[Q_MUSHROOM]._qactive == QUEST_ACTIVE && quests[Q_MUSHROOM]._qmsg == TEXT_MUSH10 && PlrHasItem(p, IDI_BRAIN, &i) != NULL) {
RemoveInvItem(p, i);
SpawnQuestItem(IDI_SPECELIX, towner[t]._tx, towner[t]._ty + 1, 0, 0);
@ -1118,6 +1133,8 @@ void TalkToTowner(int p, int t)
#ifdef HELLFIRE
} else if (towner[t]._ttype == TOWN_FARMER) {
if (!qtextflag) {
qt = 277;
t2 = 1;
switch (quests[Q_FARMER]._qactive) {
case 0:
if (PlrHasItem(p, IDI_RUNEBOMB, &i)) {
@ -1126,8 +1143,15 @@ void TalkToTowner(int p, int t)
quests[Q_FARMER]._qvar1 = 1;
quests[Q_FARMER]._qlog = 1;
quests[Q_FARMER]._qmsg = TEXT_FARMER1;
break;
} else if (!plr[myplr]._pLvlVisited[9] && plr[myplr]._pLevel < 15) {
qt = !PlrHasItem(p, IDI_RUNEBOMB, &i) ? TEXT_FARMER3 : TEXT_FARMER2;
qt = 309;
if (plr[myplr]._pLvlVisited[2])
qt = 281;
if (plr[myplr]._pLvlVisited[5])
qt = 308;
if (plr[myplr]._pLvlVisited[7])
qt = 310;
} else {
qt = TEXT_FARMER1;
quests[Q_FARMER]._qactive = 2;
@ -1135,7 +1159,14 @@ void TalkToTowner(int p, int t)
quests[Q_FARMER]._qlog = 1;
quests[Q_FARMER]._qmsg = TEXT_FARMER1;
SpawnRuneBomb(towner[t]._tx + 1, towner[t]._ty);
t2 = 1;
break;
}
case 2:
if (PlrHasItem(p, IDI_RUNEBOMB, &i))
qt = TEXT_FARMER2;
else
qt = TEXT_FARMER3;
break;
case 1:
if (PlrHasItem(p, IDI_RUNEBOMB, &i)) {
@ -1162,16 +1193,15 @@ void TalkToTowner(int p, int t)
quests[Q_FARMER]._qlog = 1;
quests[Q_FARMER]._qmsg = TEXT_FARMER1;
SpawnRuneBomb(towner[t]._tx + 1, towner[t]._ty);
t2 = 1;
}
break;
case 2:
qt = !PlrHasItem(p, IDI_RUNEBOMB, &i) ? TEXT_FARMER3 : TEXT_FARMER2;
break;
case 3:
qt = TEXT_FARMER4;
SpawnRewardItem(IDI_AURIC, towner[t]._tx + 1, towner[t]._ty);
quests[Q_FARMER]._qactive = 10;
quests[Q_FARMER]._qlog = 0;
t2 = 1;
break;
case 10:
qt = -1;
@ -1182,7 +1212,10 @@ void TalkToTowner(int p, int t)
break;
}
if (qt != -1) {
if (t2)
InitQTextMsg(qt);
else
PlaySFX(alltext[qt].sfxnr);
}
if (gbMaxPlayers != 1) {
NetSendCmdQuest(TRUE, Q_FARMER);
@ -1190,6 +1223,8 @@ void TalkToTowner(int p, int t)
}
} else if (towner[t]._ttype == TOWN_COWFARM) {
if (!qtextflag) {
qt = 297;
t2 = 1;
if (PlrHasItem(p, IDI_GREYSUIT, &i)) {
qt = TEXT_JERSEY7;
RemoveInvItem(p, i);
@ -1211,13 +1246,14 @@ void TalkToTowner(int p, int t)
quests[Q_JERSEY]._qactive = 7;
break;
case 1:
qt = TEXT_JERSEY5;
qt = TEXT_JERSEY1;
quests[23]._qactive = 7;
break;
case 2:
qt = TEXT_JERSEY1;
qt = TEXT_JERSEY5;
break;
case 3:
qt = TEXT_JERSEY5;
qt = TEXT_JERSEY1;
break;
case 7:
qt = TEXT_JERSEY2;
@ -1229,20 +1265,20 @@ void TalkToTowner(int p, int t)
break;
case 9:
if (!plr[myplr]._pLvlVisited[9] && plr[myplr]._pLevel < 15) {
switch (random_(0, 4)) {
case 0:
switch (random_(0, 4) + 9) {
case 9:
qt = TEXT_JERSEY9;
break;
case 1:
case 10:
qt = TEXT_JERSEY10;
break;
case 2:
case 11:
qt = TEXT_JERSEY11;
break;
default:
qt = TEXT_JERSEY12;
break;
}
break;
} else {
qt = TEXT_JERSEY4;
quests[Q_JERSEY]._qactive = 2;
@ -1250,6 +1286,7 @@ void TalkToTowner(int p, int t)
quests[Q_JERSEY]._qmsg = TEXT_JERSEY4;
quests[Q_JERSEY]._qlog = 1;
SpawnRuneBomb(towner[t]._tx + 1, towner[t]._ty);
t2 = 1;
}
break;
default:
@ -1259,7 +1296,10 @@ void TalkToTowner(int p, int t)
}
}
if (qt != -1) {
if (t2)
InitQTextMsg(qt);
else
PlaySFX(alltext[qt].sfxnr);
}
if (gbMaxPlayers != 1) {
NetSendCmdQuest(TRUE, Q_JERSEY);
@ -1267,9 +1307,10 @@ void TalkToTowner(int p, int t)
}
} else if (towner[t]._ttype == TOWN_GIRL) {
if (!qtextflag) {
qt = 282;
t2 = 0;
if (!PlrHasItem(p, IDI_THEODORE, &i) || quests[Q_GIRL]._qactive == 3) {
switch (quests[Q_FARMER]._qactive) {
switch (quests[Q_GIRL]._qactive) {
case 0:
qt = TEXT_GIRL2;
quests[Q_GIRL]._qactive = 2;

2
SourceX/DiabloUI/mainmenu.cpp

@ -40,7 +40,7 @@ void mainmenu_Load(char *name, void (*fnSound)(char *file))
vecMenuItems.push_back(new UiListItem("Multi Player", MAINMENU_MULTIPLAYER));
vecMenuItems.push_back(new UiListItem("Replay Intro", MAINMENU_REPLAY_INTRO));
#ifdef HELLFIRE
vecMenuItems.push_back(new UiListItem("Support", MAINMENU_SHOW_CREDITS));
vecMenuItems.push_back(new UiListItem("Support", MAINMENU_SHOW_SUPPORT));
vecMenuItems.push_back(new UiListItem("Credits", MAINMENU_SHOW_CREDITS));
vecMenuItems.push_back(new UiListItem("Exit Hellfire", MAINMENU_EXIT_DIABLO));
#else

13
enums.h

@ -2759,11 +2759,14 @@ typedef enum _music_id {
typedef enum _mainmenu_selections {
MAINMENU_SINGLE_PLAYER = 1,
MAINMENU_MULTIPLAYER = 2,
MAINMENU_REPLAY_INTRO = 3,
MAINMENU_SHOW_CREDITS = 4,
MAINMENU_EXIT_DIABLO = 5,
MAINMENU_ATTRACT_MODE = 6,
MAINMENU_MULTIPLAYER,
MAINMENU_REPLAY_INTRO,
#ifdef HELLFIRE
MAINMENU_SHOW_SUPPORT,
#endif
MAINMENU_SHOW_CREDITS,
MAINMENU_EXIT_DIABLO,
MAINMENU_ATTRACT_MODE,
} _mainmenu_selections;
typedef enum _selhero_selections {

Loading…
Cancel
Save