Tworzenie gry strategicznej typu Dune 2.


Poprzedni wpis (3) | Następny wpis (5)

Wpis 4 (25.09.2016)

Temat: Podsumowanie 1

Pora na podsumowanie dotychczasowej pracy oraz zestawienie i uzupełnienie opracowanych struktur oraz funkcji.

1. Mapa

Punktem pierwszym jest mapa, czyli prostokątna tablica złożona z pól, na której toczy się rozgrywka. Każde pole posiada rodzaj terenu oraz powiązany budynek. Uwaga: Grafika poszczególnych pól jest liczona na podstawie terenu sąsiadujących pól, ale nie należy do struktury Mapa.

Atrybuty mapy przechowuję w odrębnej strukturze. Dodajmy do tej struktury nazwę mapy oraz położenie mapy na świecie (krainę), co decyduje o zestawie kafelków.

#define NAZWA_MAX (32) /* Rozmiar buforu na nazwę */
#define BRAK (~0) /* Stała opisująca brak (np. budynku na polu) */

struct ParametryMapy
{
    UBYTE szerokosc, wysokosc; /* Wymiary */
    UBYTE nazwa[NAZWA_MAX]; /* Nazwa mapy (np. do identyfikacji pliku) */
    UBYTE kraina; /* W jakiej krainie ta mapa jest położona */
};
Pojedyńcze pole posiada typ terenu, nr budynku (lub stałą BRAK, oznaczającą brak budynku na tym polu). Wprowadźmy jeszcze na zapas efekt dodatkowy na polu, który przyda się później (np. krater od wybuchu lub koleiny utworzone przez przejeżdżająćy pojazd).
enum /* Typy terenu */
{
    ZIEMIA, PUSTYNIA, TRAWA, SKALA, WODA, BAGNA, TYPY_TERENU
};

struct Pole
{
    UBYTE typ_terenu; /* Rodzaj terenu na tym polu */
    UBYTE indeks_budynku; /* Budynek powiązany z polem */
    UBYTE efekt_dodatkowy; /* Efekt dodatkowy typu koleiny */
};
Warte odnotowania jest to, że indeks budynku i efekt dodatkowy to jest liczba, będąca odniesieniem do zewnętrznej tablicy, składającej się odpowiednio ze struktur Budynek i struktur EfektDodatkowy, które dokładniej opisują te rzeczy. Struktura Mapy zawiera w sobie zarówno parametry mapy, jak i wskaźnik na aktualną tablicę pól.
struct Mapa
{
    struct ParametryMapy parametry;
    struct Pole *pola;
};
Struktura Mapy jest tworzona na podstawie struktury ParametryMapy.

2. Budynki

Budynki są powiązane z mapą, ale stanowią też oddzielne byty. Posiadają szereg własnych atrybutów, jak typ, przynależność, punkty życia, czy postęp budowy lub szkolenia, jak również status (aktualny stan budynku).

enum /* Typy budynków */
{
    PLAC_BUDOWY, ELEKTROWNIA, RAFINERIA, TYPY_BUDYNKOW
};

enum /* Status budynku */
{
    WZNOSZONY, DZIALA, REPEROWANY, MODERNIZOWANY
};

struct ParametryBudynku
{
    UBYTE typ; /* Typ budynku */
    UBYTE x, y; /* Współrzędne lewego górnego pola budynku */
    UBYTE strona; /* Do kogo należy budynek */
};

struct Budynek
{
    struct ParametryBudynku parametry;
    UBYTE punkty_zycia; /* Aktualne punkty wytrzymałości budynku */
    UBYTE postep_budowy; /* Aktualny postęp w budowie jednostki/budynku */
    UBYTE status; /* Aktualny status budynku (jest wznoszony, reperowany,
        modernizowany itp.) */
};
Co więcej wiele budynków pracuje na bieżąco i wymaga cyklicznej obsługi (np. reperacji, szkolenia jednostek itp.).

Każdy typ budynku ma pewne cechy charakterystyczne. Dlatego tworzę strukturę CechyBudynku, który zawiera te własności:

struct CechyBudynku
{
    UBYTE nazwa[NAZWA_MAX];
    UWORD cena;
    UWORD max_punkty_zycia;
    UBYTE szerokosc, wysokosc;
};

3. Wydarzenia i polecenia

Przykład cyklicznej obsługi Budynków wymaga wprowadzenia czegoś na kształt cyklicznie wykonywanych funkcji. Wprowadzam zatem Wydarzenia, które, jak się później okazało - są dużo bardziej uniwersalne i przydatne.

Ustalam jednostkę czasu w grze. Następnie ustalam maksymalny odstęp czasu, mierzony w tych jednostkach, między dwoma dowolnymi wydarzeniami okresowymi.

Następnie tworzę tablicę o rozmiarze tego okresu, której elementy to są listy akcji do wykonania w danej jednostce czasu. Dzięki temu mogę wprowadzić wydarzenia okresowe.

Wydarzenia jednak nie tylko muszą być powodowane upłynięciem czasu. Pochodzą też z innych źródeł, np. zakończenie budowania budynku może być wydarzeniem i powodować jakąś akcję.

Trzecim typem wydarzeń to polecenia. Polecenia wydaje źródło według własnych potrzeb. Przykładowo gracz (żywy lub komputerowy) może zlecić budowania jednostki. Wówczas polecenie wędruje do adresata i ten zobowiązany jest wykonać polecenie, o ile to możliwe.

Zdefiniujemy strukturę Akcji, czyli czynności wykonywanej po zajściu Wydarzenia.

struct Akcja
{
    LONG (*akcja)(struct Wydarzenie *wydarzenie, APTR dane); /* Wywołaj tą funkcję, 
	by podjąć akcję */
    APTR dane; /* Dane pomocnicze ustalane przez funkcję dodającą Akcję do kolejki */
    struct Akcja *nastepna; /* Następna Akcja powiązana z tym samym wydarzenie */
};
Akcja powiązana jest z Wydarzeniem albo Poleceniem, które ma postać następującą:
enum /* Podstawowe typy wydarzeń */
{
    WYDARZENIE_OKRESOWE, /* Wydarzenie co pewien odcinek czasu (może
        też być zleceniem) */
    POLECENIE, /* Wydarzenie będące zleceniem */
    WYDARZENIE /* Wydarzenie powodowane jakimiś stałymi czynnikami
        (np. "Zakończenie budowy") */
};

struct Wydarzenie
{
    UBYTE typ; /* Typ wydarzenia */
    APTR dane; /* Dane dodatkowe wydarzenia */
};
Wydarzenie typu "Zakończenie budowy" zachodzi w pewnych stałych okolicznościach. Nie ma jednakże bezpośredniego adresata. Obiekt musi zarejestrować otrzymywanie tego typu wydarzeń, dopiero wówczas będzie informowany o zajściu danego wydarzenia.

Polecenia i rodzaje wydarzeń mogą być różnorakie. Ich interpretacja należy do funkcji składowej struktury Akcja. Opiszmy jednakże przykładowe zestawienie dla Poleceń wydawanych budynkom:

struct Polecenie
{
    UBYTE polecenie; /* Typ polecenia */
    UBYTE szczegoly; /* Szczegóły polecenia */
    APTR dane; /* Dane dodatkowe (np. typ budynku do wzniesienia) */
};

enum /* Typy poleceń */
{
    BUDOWA, REPERACJA, MODERNIZACJA
};

enum
{
    ROZPOCZNIJ, WSTRZYMAJ, KONTYNUUJ, ANULUJ
};
Podsumowanie: Mając opracowane te elementy można już zrobić system Mapy, Budynków oraz Wydarzeń i Poleceń. Trzeba jeszcze zapisać prototypy funkcji (jeszcze bez implementacji). Uczynię to w kolejności.

Robert Szacki e-mail: robert.szacki(małpa)gmail.com