diff --git a/Telegram/Resources/animations/dice/dice_1.tgs b/Telegram/Resources/animations/dice/dice_1.tgs
new file mode 100644
index 0000000000..36506f7108
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_1.tgs differ
diff --git a/Telegram/Resources/animations/dice/dice_2.tgs b/Telegram/Resources/animations/dice/dice_2.tgs
new file mode 100644
index 0000000000..ac0de6ff5a
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_2.tgs differ
diff --git a/Telegram/Resources/animations/dice/dice_3.tgs b/Telegram/Resources/animations/dice/dice_3.tgs
new file mode 100644
index 0000000000..1362b087fe
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_3.tgs differ
diff --git a/Telegram/Resources/animations/dice/dice_4.tgs b/Telegram/Resources/animations/dice/dice_4.tgs
new file mode 100644
index 0000000000..60b7c6d8ce
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_4.tgs differ
diff --git a/Telegram/Resources/animations/dice/dice_5.tgs b/Telegram/Resources/animations/dice/dice_5.tgs
new file mode 100644
index 0000000000..77bb708982
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_5.tgs differ
diff --git a/Telegram/Resources/animations/dice/dice_6.tgs b/Telegram/Resources/animations/dice/dice_6.tgs
new file mode 100644
index 0000000000..f151a226e7
Binary files /dev/null and b/Telegram/Resources/animations/dice/dice_6.tgs differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 70dfc14f29..b9410dfe57 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2477,7 +2477,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stake_game_beta" = "Beta";
"lng_stake_game_about" = "A limited play-test of the upcoming emoji mini-game platform for a small group of users.";
"lng_stake_game_results" = "Results and Returns";
-"lng_stake_game_resets" = "A streak resets after 3 sixes or a stake change.";
+"lng_stake_game_resets" = "A streak resets after 3 {emoji} or a stake change.";
"lng_stake_game_your" = "Your stake";
"lng_stake_game_save_and_roll" = "Save and Roll";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 87ba31c8c9..6c7e4c94c7 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -67,6 +67,13 @@
../../animations/dice/slot_pull.tgs
../../animations/dice/winners.tgs
+ ../../animations/dice/dice_1.tgs
+ ../../animations/dice/dice_2.tgs
+ ../../animations/dice/dice_3.tgs
+ ../../animations/dice/dice_4.tgs
+ ../../animations/dice/dice_5.tgs
+ ../../animations/dice/dice_6.tgs
+
../../animations/star_reaction/appear.tgs
../../animations/star_reaction/center.tgs
../../animations/star_reaction/select.tgs
diff --git a/Telegram/SourceFiles/history/view/media/history_view_dice.cpp b/Telegram/SourceFiles/history/view/media/history_view_dice.cpp
index c65242d903..9d39c39ae9 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_dice.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_dice.cpp
@@ -47,6 +47,7 @@ Dice::Dice(not_null parent, not_null dice)
if (const auto outcome = _dice->outcome()) {
_outcomeSet = true;
+ _outcomeValue = _dice->value();
_outcomeNanoTon = outcome.nanoTon;
updateOutcomeMessage();
}
@@ -75,7 +76,7 @@ void Dice::updateOutcomeMessage() {
lt_amount,
amount,
tr::marked))
- : !_dice->value()
+ : !_outcomeValue
? tr::lng_action_stake_game_loading(tr::now, tr::marked)
: (out
? tr::lng_action_stake_game_nothing_you(tr::now, tr::marked)
@@ -103,11 +104,15 @@ bool Dice::updateItemData() {
const auto outcome = _dice->outcome();
const auto outcomeSet = !!outcome;
const auto outcomeNanoTon = outcomeSet ? outcome.nanoTon : 0;
- if (_outcomeSet == outcomeSet && _outcomeNanoTon == outcomeNanoTon) {
+ const auto outcomeValue = _dice->value();
+ if (_outcomeSet == outcomeSet
+ && _outcomeNanoTon == outcomeNanoTon
+ && _outcomeValue == outcomeValue) {
return false;
}
_outcomeSet = outcomeSet;
_outcomeNanoTon = outcomeNanoTon;
+ _outcomeValue = outcomeValue;
updateOutcomeMessage();
return true;
}
diff --git a/Telegram/SourceFiles/history/view/media/history_view_dice.h b/Telegram/SourceFiles/history/view/media/history_view_dice.h
index a3292b0b64..37d7238fb2 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_dice.h
+++ b/Telegram/SourceFiles/history/view/media/history_view_dice.h
@@ -53,6 +53,7 @@ private:
std::optional _start;
std::optional _end;
int64 _outcomeNanoTon = false;
+ int _outcomeValue = 0;
mutable bool _showLastFrame = false;
mutable bool _drawingEnd = false;
bool _outcomeSet = false;
diff --git a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
index f29dd06375..7f64486d3a 100644
--- a/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
+++ b/Telegram/SourceFiles/ui/boxes/emoji_stake_box.cpp
@@ -8,7 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/emoji_stake_box.h"
#include "base/object_ptr.h"
+#include "chat_helpers/stickers_lottie.h"
#include "data/components/credits.h"
+#include "data/stickers/data_custom_emoji.h"
+#include "data/data_document.h"
+#include "data/data_session.h"
#include "info/channel_statistics/earn/earn_icons.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
@@ -18,12 +22,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/controls/ton_common.h"
#include "ui/text/custom_emoji_helper.h"
#include "ui/text/custom_emoji_text_badge.h"
+#include "ui/text/text_lottie_custom_emoji.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/fields/number_input.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
+#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "styles/style_calls.h" // confcallJoinBox
#include "styles/style_chat.h"
@@ -53,15 +59,140 @@ namespace {
const auto &size = st::settingsCloudPasswordIconSize;
auto icon = Settings::CreateLottieIcon(
box->verticalLayout(),
- { .name = u"dice_idle"_q, .sizeOverride = { size, size } },
+ { .name = u"dice_6"_q, .sizeOverride = { size, size } },
st::settingLocalPasscodeIconPadding);
const auto animate = std::move(icon.animate);
box->showFinishes() | rpl::take(1) | rpl::on_next([=] {
- animate(anim::repeat::loop);
+ animate(anim::repeat::once);
}, box->lifetime());
return std::move(icon.widget);
}
+[[nodiscard]] object_ptr MakeTable(
+ not_null parent,
+ const EmojiGameStakeArgs &args) {
+ auto result = object_ptr(parent);
+ const auto raw = result.data();
+
+ struct State {
+ std::array, 6> dices;
+ std::array multiplicators;
+ };
+ const auto state = raw->lifetime().make_state();
+ const auto serialize = [&](int milliReward) {
+ return QString(QChar(0xD7)) + QString::number(milliReward / 1000.);
+ };
+ const auto esize = st::largeEmojiSize;
+ for (auto i = 0; i != 6; ++i) {
+ state->dices[i] = Lottie::MakeEmoji({
+ .name = u"dice_%1"_q.arg(i + 1),
+ .sizeOverride = QSize(esize, esize),
+ }, [=] { raw->update(); });
+ const auto value = (i < args.milliRewards.size())
+ ? args.milliRewards[i]
+ : 0;
+ state->multiplicators[i].setText(
+ st::semiboldTextStyle,
+ serialize(value));
+ }
+ state->multiplicators[6].setText(
+ st::semiboldTextStyle,
+ serialize(args.jackpotMilliReward));
+
+ raw->paintOn([=](QPainter &p) {
+ auto path = QPainterPath();
+ const auto &st = st::defaultTable;
+ const auto border = st.border;
+ const auto half = border / 2.;
+ const auto add = QMargins(border, border, border, border);
+ const auto inner = raw->rect().marginsRemoved(add);
+ path.addRoundedRect(inner, st.radius, st.radius);
+ {
+ const auto y = border + inner.height() / 2.;
+ path.moveTo(border + half, y);
+ path.lineTo(border + inner.width() - half, y);
+ }
+ {
+ const auto x = border + inner.width() / 2.;
+ path.moveTo(x, border + half);
+ path.lineTo(x, border + inner.height() - half);
+ }
+ {
+ const auto x = border + (inner.width() - border) / 4.;
+ path.moveTo(x, border + half);
+ path.lineTo(x, border + inner.height() - half);
+ }
+ {
+ const auto x = border
+ + (inner.width() / 2.)
+ + (inner.width() - border) / 4.;
+ path.moveTo(x, border + half);
+ path.lineTo(x, border + (inner.height() / 2.) - half);
+ }
+ auto pen = st.borderFg->p;
+ pen.setWidth(st.border);
+ p.setPen(pen);
+ p.setBrush(Qt::NoBrush);
+
+ auto hq = PainterHighQualityEnabler(p);
+ p.drawPath(path);
+
+ const auto etop = -style::ConvertScale(10);
+ const auto ttop = etop + esize;
+ {
+ auto left = border + half;
+ const auto width = (inner.width() - 3 * border) / 4.;
+ auto top = border + half;
+ const auto now = crl::now();
+ for (auto i = 0; i != 7; ++i, left += width + border) {
+ if (i == 4) {
+ left -= 4 * (width + border);
+ top += border + half + inner.height() / 2.;
+ }
+ const auto x = int(base::SafeRound(left));
+ const auto y = int(base::SafeRound(top));
+ const auto right = (i < 6)
+ ? int(base::SafeRound(left + width))
+ : int(base::SafeRound(left + 2 * width + border));
+ const auto w = (right - x);
+
+ if (i < 6) {
+ state->dices[i]->paint(p, {
+ .textColor = st::windowBoldFg->c,
+ .now = now,
+ .position = QPoint(x + (w - esize) / 2, y + etop),
+ .paused = true,
+ .internal = { .forceLastFrame = true }
+ });
+ } else {
+ const auto e = state->dices.back().get();
+ for (auto j = 0; j != 3; ++j) {
+ e->paint(p, {
+ .textColor = st::windowBoldFg->c,
+ .now = now,
+ .position = QPoint(
+ x + (w - 3 * esize) / 2 + (esize * j),
+ y + etop),
+ .paused = true,
+ .internal = { .forceLastFrame = true }
+ });
+ }
+ }
+
+ p.setPen(st::windowBoldFg);
+ const auto &mul = state->multiplicators[i];
+ mul.draw(p, {
+ .position = { x + (w - mul.maxWidth()) / 2, y + ttop },
+ });
+ }
+ }
+ });
+ raw->widthValue() | rpl::on_next([=](int width) {
+ raw->resize(width, width / 3);
+ }, raw->lifetime());
+ return result;
+}
+
} // namespace
not_null AddStarsInputField(
@@ -124,10 +255,12 @@ void EmojiGameStakeBox(
box->setStyle(st::confcallJoinBox);
box->setWidth(st::boxWideWidth);
box->setNoContentMargin(true);
+ const auto container = box->verticalLayout();
+ const auto skip = st::confcallLinkHeaderIconPadding.bottom();
box->addRow(
MakeLogo(box),
- st::boxRowPadding + st::confcallLinkHeaderIconPadding,
+ st::boxRowPadding + QMargins(0, 0, 0, skip),
style::al_top);
auto helper = Text::CustomEmojiHelper();
@@ -154,13 +287,91 @@ void EmojiGameStakeBox(
box,
tr::lng_stake_game_about(tr::rich),
st::confcallLinkCenteredText),
- st::boxRowPadding,
+ st::boxRowPadding + QMargins(0, 0, 0, skip),
style::al_top
)->setTryMakeSimilarLines(true);
- const auto container = box->verticalLayout();
+ const auto sub = st::boxRowPadding - st::defaultSubsectionTitlePadding;
+ const auto subtitlePadding = QMargins(sub.left(), 0, sub.right(), 0);
+ AddSubsectionTitle(
+ container,
+ tr::lng_stake_game_results(),
+ subtitlePadding);
- AddSubsectionTitle(container, tr::lng_stake_game_results());
+ const auto half = (st::defaultTable.border + 1) / 2;
+ box->addRow(
+ MakeTable(box, args),
+ st::boxRowPadding + QMargins(-half, 0, -half, skip / 2),
+ style::al_top);
+
+ const auto six = Lottie::IconDescriptor{
+ .name = u"dice_6"_q,
+ .sizeOverride = { st::emojiSize, st::emojiSize },
+ };
+ const auto sixText = Text::LottieEmoji(six);
+ const auto sixContext = Text::LottieEmojiContext(six);
+ const auto factory = [=](
+ QStringView data,
+ const Text::MarkedContext &context) {
+ if (auto result = sixContext.customEmojiFactory(data, context)) {
+ return std::make_unique(
+ std::move(result),
+ 0,
+ true);
+ }
+ return std::unique_ptr();
+ };
+ const auto context = Text::MarkedContext{
+ .customEmojiFactory = factory,
+ };
+ const auto resets = box->addRow(
+ object_ptr(
+ box,
+ tr::lng_stake_game_resets(
+ lt_emoji,
+ rpl::single(sixText),
+ tr::rich),
+ st::confcallLinkFooterOr,
+ st::defaultPopupMenu,
+ context),
+ st::boxRowPadding,
+ style::al_top);
+ resets->setTryMakeSimilarLines(true);
+ resets->setAnimationsPausedCallback([=] {
+ return FlatLabel::WhichAnimationsPaused::All;
+ });
+
+ AddSubsectionTitle(
+ container,
+ tr::lng_stake_game_your(),
+ subtitlePadding + QMargins(0, skip, 0, sub.bottom()));
+ const auto field = AddTonInputField(container, {
+ .value = args.currentStake,
+ });
+ box->setFocusCallback([=] {
+ field->setFocusFast();
+ });
+
+ const auto submit = args.submit;
+ const auto callback = [=] {
+ const auto text = field->getLastText();
+ const auto now = Ui::ParseTonAmountString(text);
+ if (!now) {
+ field->showError();
+ } else {
+ box->closeBox();
+ submit(*now);
+ }
+ };
+ field->submits() | rpl::on_next(callback, field->lifetime());
+ const auto button = box->addButton(rpl::single(QString()), callback);
+
+ button->setText(tr::lng_stake_game_save_and_roll(
+ ) | rpl::map([=](QString text) {
+ return tr::marked(
+ QString::fromUtf8("\xf0\x9f\x8e\xb2")
+ ).append(' ').append(text);
+ }));
const auto close = CreateChild(
container,
@@ -186,39 +397,6 @@ void EmojiGameStakeBox(
st::creditsHistoryRightSkip);
balance->update();
}, balance->lifetime());
-
- box->addRow(
- object_ptr(
- box,
- tr::lng_stake_game_resets(tr::rich),
- st::confcallLinkCenteredText),
- st::boxRowPadding,
- style::al_top
- )->setTryMakeSimilarLines(true);
-
- AddSubsectionTitle(container, tr::lng_stake_game_your());
- const auto field = AddTonInputField(container, {
- .value = args.currentStake,
- });
-
- const auto submit = args.submit;
- const auto button = box->addButton(rpl::single(QString()), [=] {
- const auto text = field->getLastText();
- const auto now = Ui::ParseTonAmountString(text);
- if (!now) {
- field->showError();
- } else {
- box->closeBox();
- submit(*now);
- }
- });
-
- button->setText(tr::lng_stake_game_save_and_roll(
- ) | rpl::map([=](QString text) {
- return tr::marked(
- QString::fromUtf8("\xf0\x9f\x8e\xb2")
- ).append(' ').append(text);
- }));
}
} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/text/text_lottie_custom_emoji.cpp b/Telegram/SourceFiles/ui/text/text_lottie_custom_emoji.cpp
index e78a264c76..7791bfc2af 100644
--- a/Telegram/SourceFiles/ui/text/text_lottie_custom_emoji.cpp
+++ b/Telegram/SourceFiles/ui/text/text_lottie_custom_emoji.cpp
@@ -17,8 +17,8 @@ LottieCustomEmoji::LottieCustomEmoji(Lottie::IconDescriptor &&descriptor)
}
LottieCustomEmoji::LottieCustomEmoji(
- Lottie::IconDescriptor &&descriptor,
- Fn repaint)
+ Lottie::IconDescriptor &&descriptor,
+ Fn repaint)
: _entityData(!descriptor.name.isEmpty()
? descriptor.name
: !descriptor.path.isEmpty()