This also has the added benefit that after a restart we don't get the
same random numbers all the time because we forgot to initialize the
RNG with srand().
Applications previously had to create a GUI::Menubar object, add menus
to it, and then call GUI::Window::set_menubar().
This patch introduces GUI::Window::add_menu() which creates the menubar
automatically and adds items to it. Application code becomes slightly
simpler as a result. :^)
The game doesn't handle resize events.
It's a pretty lazy fix. The proper way would be to actually allow
the game to be resized, with some scaling trickery, but most games
here don't do that anyway, so I guess that's good enough.
AK's version should see better inlining behaviors, than the LibM one.
We avoid mixed usage for now though.
Also clean up some stale math includes and improper floatingpoint usage.
This matches basically all other Spider implementations I've played,
and makes playing much easier :)
I have left click-to-reveal in place, but mostly incase there is a
condition I've forgotten about.
Scoring is designed to mimic Microsoft's implementation - starting at
500, decreasing by 1 every move, and increasing by 100 for every full
stack.
Fixes GH-5319.
The crash happens very rarely and is hard to reproduce so it is
hard to know for certain, but I am confident this fixes it.
I previously delayed the start of the game-over animation by one
frame, but neglected to check m_start_game_over_animation_next_frame
wasn't set. This means multiple calls to start_game_over_animation()
on the same frame (or rather, before the first timer_event) would
each call Object::start_timer(). Now that we do check the flag,
that should no longer be possible.
Fixes#8122.
Better information is now shown to the player. Instructions are shown
when first loading the program, and any available scores are shown on
the game over screen.
This fixes#7792.
The visual glitches with card corners, and the screen-clear when
opening a menu, were both caused by the widgets having
`fill_with_background_color` set. We need that set most of the time,
so we just disable it for the duration of the game-over animation.
Also resove a FIXME that no longer applies. :^)
Previously, the timer started if you clicked within the game area,
whether that was on a card or not. Now, we only start when you click
on a card or otherwise attempt a move.
As a bonus, we now immediately update the status bar time indicator
on game start, instead of having to wait until 1 second has elapsed.
Previously, if a tab-move moved all the remaining cards to the
foundations, the game would not notice until you moved a card
away and then back again.
I had to add a 1-frame delay to starting the animation, so that it
would have time to re-paint the foundation in that case.
Whether or not tiles moved used to be determined by comparing new_tiles
with m_tiles. This is no longer possible since the slide is done
in-place.
This fix makes the game look through the m_sliding_tiles, which contains
previous and current position for each tile on the board, to determine
whether any tile moved at all. If not, the move is deemed unsuccessful.
Fixes#8008.
Keeping this as a separate commit as I'm not certain whether this
is a good change or not. The repeated if-else for each Foundation
stack bothered me a bit, though more so before I reduced the code
in the {}. But maybe the ifs are clearer than the loop?
Doing that also meant I could inline the move_card() code instead
of needing to make it a lambda. Again, maybe it would be better as
a lambda? I'm still figuring out the style Serenity uses, and I
know Andreas is big on expressiveness, and move_card() is more
expressive than just having the code in the loop.
The two paths did the same thing, in two different ways. Now they
are the same. :^)
Can't quite put all of the logic into
attempt_to_move_card_to_foundations() because the double-click has
to check that you clicked on the top card.
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
Now that the cards have rounded corners, draw the stack box behind the
cards with rounded corners as well. This way, the corner of the stack
box doesn't peek out from behind the cards.
The caveat here is that the "play" card stack now needs to hold a
reference to the "waste" stack beneath it so it knows when not to draw
its background on top of the waste stack. To faciliate that, the array
of card stacks is now a NonnullRefPtrVector so the play stack can store
a smart pointer to the waste stack (instead of a raw pointer or
reference).
Also shifts logic of starting game length timer into function
`start_timer_if_necessary`, so it can be called from original
mouse event handler and new `auto_move_eligible_cards_to_stacks`
When we don't have a matching card for the lead card rather than
always preferring to play hearts we should try to get rid of our
high value cards first if no other player has hearts cards higher
than what we have.
When we're the third player in a trick and we don't have a lower value
card we would previously pick a slightly higher value card. Instead
we should pick the highest value card unless there are points in the
current trick or the lead card is spades and the higher value card
we would've picked is higher than the queen and another player still
has the queen.
The rationale is that we have to take the trick anyway so we might as
well get rid of our highest value card. If the trailing player has a
lower value card of the same type we take the trick but don't gain
any points. If they don't have a card of the same type it doesn't
matter whether we play a high value or low value card.
Previously the AI would prefer playing a lead card for which no other
player had a card with a higher value even though it also had a card
for which a higher value card was still in play.
Currently, the player loses 100 points each time the waste stack is
recycled. In three-card draw mode, it's standard to only lose 20 points
after the third recycle event.
This invocation will exit immediately. There's also no reason to invoke
stop_game_over_animation here because that's the first thing that will
happen in the call to setup.
When an animation is stopped the cards should be moved to their final
position anyway. Otherwise they might end up getting stuck in the
middle of the animation.
This changes the game so that more than one hand can be played. Once
one player has 100 or more points the game ends. A score card is shown
between each hand.
Fixes#7374.
Instead of picking the card with the lowest value we should pick the
card with the highest value for which we know no lower value card is
in play anymore and that someone else still has an even higher value
card.
Problem:
- `static` variables consume memory and sometimes are less
optimizable.
- `static const` variables can be `constexpr`, usually.
- `static` function-local variables require an initialization check
every time the function is run.
Solution:
- If a global `static` variable is only used in a single function then
move it into the function and make it non-`static` and `constexpr`.
- Make all global `static` variables `constexpr` instead of `const`.
- Change function-local `static const[expr]` variables to be just
`constexpr`.
Currently, it is possible for the player to drag an entire stack
of cards to the foundation stack, provided the top card of the stack
(i.e the "root" card) can be dropped onto the foundation stack.
This causes an invalid state where, e.g, red cards end up in a
black foundation stack, or vice versa.
- Make the ball chill out a bit by reducing its x velocity by 1.
- Make the AI dumber. It now relaxes until the ball is coming towards it
and is in its half of the court.
Just because you're the player, doesn't mean you can cheat ;)
- Limit paddle speed when following mouse cursor.
- Unhide pointer -- can't grab it, can't see when it's about to leave
the window otherwise.
- Add last pointer y position indication for the player paddle.
- Prevent the paddle from following the mouse when Up or Down is being
held (avoid controls fighting).
Begin and end triggers for AI paddle to start moving prevent it from
continually jerking around the ball's position. It still moves in steps
of paddle.speed though, but overall experience is smoother.
Player 1's score used to be score's width offset to the right. Now it's
score_margin away from the divider line, same as player 2's.
Also factored out score margin and increased it from 2px to 5px just
to make it look a little nicer.
Also tweak its text in the settings window to be clearer on what this
check box is doing. Seemed a bit confusing that settings changes
required an extra action to persist.
Currently, each time you open the settings window in 2048, it displays
the default values rather than the current values. This is confusing, so
display the current values instead.
Maximizing the board population still takes priority, but if there are
tile generator "moves" that result in equivalent board population after
a player move, the one with the lowest score is selected.
Make everything signed so that we don't have to deal with silly casting
issues thoughout the Chess code. I am unsure if this affects the chess
AI negatively, it seems just as "intelligent" before and after this
change :^)
This is not only easier to comprehend code-wise and avoids some function
overloads, but also makes resizing the board while preserving game state
*a lot* easier. We now no longer have to allocate a new board on every
resize, we just grow/shrink the individual row vectors.
Also fixes a crash when clicking the board widget outside of the drawn
board area.
While the waste stack and the playable card on top of the waste stack
are collectively referred to as the "waste", it's programatically nice
to separate them to enable 3-card-draw mode. In that mode, the playable
stack will contain 3 cards with a slight x-axis shift, while the waste
stack underneath will remain unshifted. So rather than introducing some
ugly logic to CardStack to handle this, it's more convenient to have a
separate stack on top of the waste stack.
Since applications using Core::EventLoop no longer need to create a
socket in /tmp/rpc/, and also don't need to listen for incoming
connections on this socket, we can remove a whole bunch of pledges!
Not sure why some menus did have one and others didn't, even in the
same application - now they all do. :^)
I added character shortcuts to some menu actions as well.
The timer is no longer used to trigger a paint event for all updates; it
is only used to paint the new-game and game-over animations. So only run
the timer during those events.
A series of events led to this change: The goal is to add more widgets
to the Solitaire GML, such as a GUI::Statusbar. To do so without this
change, the window ends up with some black artifacts between the main
Solitaire frame and the added elements, because the GML specifies the
main widget to have fill_with_background_color=false. However, setting
that property to true results in the background color of the widget
interferring with the Solitaire frame trying to manually paint its
background green. This results in flickering and some elements in the
Solitaire frame being painted over by the main background color.
To avoid all of that behavior, this sets fill_with_background_color=true
and the Solitaire frame's background color to green in the GML. Further,
the frame now only queues a paint update on the specific Gfx::Rect areas
that need to be updated. This also means we no longer need to track if a
stack of cards is dirty, because we only trigger a paint event for dirty
stacks.
No functionial change here, but this more easily allows for adding GUI
elements to the Solitaire window. This nests the SolitaireWidget as a
child of the main window's widget so that the SolitaireWidget does not
color the entire window green when it paints its background.