Implementacje - manio143/ShadowsOfShadows GitHub Wiki
Popatrzmy na ciekawsze elementy tego projektu. Po pierwsze, możemy zobaczyć podział na pewne moduły: Consoles
, Entities
, Generators
, Items
, Serialization
, itd. Moduły Entities
oraz Items
opisują elementy gry: encje, które zobaczymy na ekranie oraz przedmioty, które znajdziemy w skrzyniach i które możemy dodać do naszego ekwipunku.
Dla nas ciekawe będą głównie trzy moduły: Consoles
, Generators
i Serialization
.
Consoles
- opisuje obiekty konsoli (dziedziczące poSadConsole.Console
) oraz większość logiki grySerialization
- opisuje mechanizm zapisywania stanu gryGenerators
- opisuje losowe generatory przedmiotów, postaci i pokoi
Consoles
Naszym głównym ekranem jest obiekt klasy Screen
, który tworzy pozostałe konsole i kontroluje ich aktualizację.
Jakoś tak wyszło, że pozostałe konsole utworzyły hierachię, co nie jest najlepszym przykładem dziedziczenia, ale było dość wygodne ze względu na reużywalność kodu.
MessageConsole > MenuConsole > StartScreen
W MessageConsole
jest logika funkcjonowania konsoli w oparciu o wiadomości, które są zdefiniowane w pliku Messages.cs
. Mamy kilka rodzajów wiadomości:
SimpleMessage
- wypisuje na konsolę tekst i go tam zostawia, dopóki nie zostanie nadpisany kolejną wiadomościąWaitMessage
- wypisuje na konsolę tekst i czeka aż zostanie naciśnięta spacja lub enterChoiceMessage
- abstrakcyjna klasa zawierająca logikę wyświetlania zestawu opcji do wyboruQuestionMessage
- wybieramy odpowiedź na pytanie poprzez typ EnumaChestMessage
- wybieramy przedmioty, które weźmiemy do ekwipunkuEquipmentMessage
- wybieramy przedmioty z ekwipunku, które spożyjemyTimeoutMessage
- wypisuje na konsolę tekst i czeka pewien czas
W MessageConsole
istnieje kolejka, do której dodajemy kolejne wiadomości, a następnie są one wyświetlane zgodnie z ich implementacją. Po zakończeniu wiadomości, czyli ustawieniu flagi Finished
, wykonujemy akcję opisaną w polu PostProcessing
i zdejmujemy kolejną wiadomość z kolejki w celu jej wyświetlenia.
MenuConsole
korzysta z tych samych mechanizmów, dlatego dziedziczy po MessageConsole
, a do tego zawiera implementację akcji i wyświetlania pozycji Menu oraz wyświetlanie statystyk gracza.
Serialization
W klasie GameState
znajduje się wszystko co związane ze stanem gry: lista pokoi, gracz, generator pokoi, etc. Tą klasę serializujemy, żeby zapisać nasz stan gry. Do tego używamy biblioteki YamlDotNet.
W klasie Serializer
mamy metodę Save(slot)
i Load(slot)
, która zamienia nam enum SaveSlot
na ścieżkę do pliku i zapisuje do niego lub odczytuje z niego zserializowane dane. Dodatkowo kompresujemy nasz plik YAMLowy za pomocą GZipStream
, żeby zajmował mniej miejsca i nie był łatwo edytowalny dla zwykłego użytkownika.
Dlaczego YamlDotNet? Próbowałem również innych serializatorów, ale tym najprościej osiągnąłem to co chciałem, choć nie jestem z niego w stu procentach zadowolony (np. musiałem upublicznić kilka prywatnych pól i zmienić je na własności). Ogólnie czuję, że na tej płaszczyźnie jeszcze brakuje nam serializatora dla dotnetu z dużą konfigurowalnością.
Generators
Nie tu jakiejś skomplikowanej magii. W przypadku przedmiotów i postaci, bierzemy wszystkie typy danego rodzaju i losujemy jeden z nich z prawdopodobieństwem opisanym przez dystrybucję z biblioteki MathNet. Następnie tworzymy jego instancję i ją zwracamy.
Z pokojem jest nieco inaczej. Każdy pokój musi dotykać poprzedniego tak żeby można było przechodzić drzwiami między nimi. Więc generator pamięta ostatni pokój. Najpierw generujemy prostokąt pokoju - ściany, a potem z dystrybucją Bernoulliego losujemy do 6 potworów i skrzyń w pokoju i losowo je w nim umieszczamy.