Střední průmyslová škola elektrotechnická, Praha 10, V Úžlabině 320
ONLINE TEXTOVÁ HRA
Praktická zkouška z odborných předmětů
Autor:
Jan Svatoš
Studijní obor:
26-47-M/002 Elektronické počítačové systémy
Třída:
E4.C
Školní rok:
2007/2008
Vedoucí práce:
Ing. Lenka Suchánková
Konzultant práce:
Lukáš Masopust 1 / 50
Čestné prohlášení: “Prohlašuji, že jsem tuto práci vypracoval samostatně a použil jsem literárních pramenů a informací, které cituji a uvádím v seznamu použité literatury a zdrojů informací. Souhlasím s využitím díla pro školu.” V Praze dne…………….
…………………………... podpis autora (jméno a příjmení) 2 / 50
Anotace Náplní práce je vytvořit on-line textovou hru s několika jednoduchými grafickými prvky. Hra je vytvořena pro internetové prohlížeče, a tak je možné ji hrát kdekoliv z jakéhokoliv počítače s přístupem na internet. Název hry je "The Ages", neboli volně přeloženo "Staré časy". Účelem hry je spravovat svou provincii, prosperovat, dobývat a konečně porazit Pána temnot a jeho armádu. Hráč má na výběr různé jednotky a budovy, které se více čí méně liší. Hra je tahová a jeden tah je přidán každých dvacet minut. To znamená, že hráč nemusí u hry neustále sedět. The goal of this is to create an on-line text-based game with a few simple graphic things. The game is created for web browsers, so it can be played anywhere with any computer with internet access. It is called "The Ages", or say "The old ages". The goal of the game is to overlook your province, prosper, conquer and finaly to defeat the Lord of darkness and his army. The player can choose different types of units, which differs more or less. The game is turnbased and one turn is added for each twenty minutes. It means that the player doesn't have to sit all the time by the computer and play the game.
1.1. Vytvořte on-line textovou hru, ktera bude zahrnovat následující akce: a) Registrace uživatelů b) Validace registrace přes e-mail c) Stavba a bourání budov d) Těžba surovin e) Výroba zbraní pro jednotky f) Rekrutování armády g) Vylepšování výzbroje (ozbrojování) jednotek armády h) Boj mezi hráči i) Pošta mezi hráči
1.2. Součástí práce bude manuál s návodem na ovládání a s pravidly hry včetně názorných screenshotů obrazovek.
1.3. Pokyny pro vypracování: 1. Skripty a stylyl vytvářejte s pomocí freeware programu PSPad. 2. Hráč bude hru ovládat pomocí jednoduchých formulářů typu „počet jednotek“ a „odeslat“. 3. Hra může umožnit tvorbu aliancí (sdružení více hráčů) a vzájemnou podporu hráčů jedné aliance v podobě půjčování jednotek a posílání surovin. 4. Ve hře je možno přidat aukci, kde budou jednotliví hráči prodávat a kupovat své jednotky, suroviny, zbraně a půdu. 5. Layout bude vytvořen pomocí CSS a jen nezbytně nutné formuláře tabulkami, bude graficky jednoduchý a nenáročný na oči (tmavé barvy) z
5 / 50
důvodu nočního či dlouhého hraní a převahy textu. 6. Vedle textových odkazů menu budou malé ikony těchto akcí. 7. Protokol maturitní práce bude splňovat formální náležitosti dle http://skola.uzlabina.cz/dlouhodoba_maturita.
6 / 50
2. Teoretický rozbor 2.1. Technologie Vzhledem k tomu, že jsem student, rozhodl jsem se použít Freeware programy nebo Trial (volně stažitelné, časově omezené) verze placených programů. Pro veškeré kódování jsem použil program PSPad, který umí zvýrazňovat jak CSS, tak PHP a HTML syntax, což je velice výhodné pro snazší orientaci v kódu při vývoji jakéhokoli projektu. Pro grafické zpracování jsem použil studentskou verzi programu Adobe Photoshop 8.0. Testoval jsem v prohlížečích Opera, FireFox a MS Internet Explorer 6.
2.2. Rozbor hry Hru jsem zasadil do prostředí středověku, přesněji fantasy. Tomu odpovídají názvy, typy a možnosti jednotek. Původně jsem chtěl vytvořit více ras a povolání, ale vzhledem k náročnosti jsem vše zredukoval na rasu jednu a povolání zrušil kompletně. Hráč tedy hraje za člověka a spravuje svou provincii. Jednotek je více typů - kombinace pozemní / letecká a bojová / střelecká. Jednotky se liší svou cenou, útokem, obranou, náročností na údržbu a mnoha dalšími vlastnostmi detailně popsanými v kapitole 3.1.5 Struktura tabulky 'Units'. Hra je tvořena pomocí PHP skriptů a MySQL databáze. Hra je online, tahová a je možné ji hrát z jakéhokoliv počítače s připojením na internet a libovolným internetovým prohlížečem. Veškeré údaje se ukládají na jeden server do databáze. Chtěl jsem vytvořit co možná nejflexibilnější a nejpřístupnější systém, a tak jsem vytvořil celkem jednoduchou, ale efektivní strukturu. Použil jsem dvě tabulky jak pro jednotky, tak pro budovy. To mi dovoluje měnit pružně vlastnosti jednotek a budov, aniž bych jakkoliv měnil skripty. Pro hráče a poštu mám tabulky zvlášť. Většinu kontrol a zpracování informací řeším přes skript zpracuj.php, který je víceúčelový a dle zaslaného parametru action vykoná vždy chtěnou operaci.
7 / 50
2.3. Pravidla hry Následující výčet pravidel hry by měl zamezit nejčastějším problémům s administrací a překračovaní zákonů ČR. S pravidly musí hráč souhlasit při registraci nového účtu. •
Hráč smí vlastnit pouze jednu provincii.
•
Hráč může hrát po neomezenou dobu.
•
Hráč je povinen upozornit na jakoukoliv odchylku od pravidel.
•
Herní účty jsou majetkem zřizovatele a hráčům jsou pouze propůjčeny po dobu hraní.
•
Administrátoři si vyhrazují právo smazat, či jakkoliv upravit libovolný účet v souladu s pravidly.
•
Administrátoři si vyhrazují právo změnit pravidla, přičemž budou hráči o změnách informováni minimálně týden před uvedením v platnost.
•
Je zakázáno zneužívat chyb ve hře a napomáhání v tomto jiným hráčům.
•
Je zakázáno jakkoliv urážet, napadat či zesměšňovat administrátory.
•
Je zakázáno propagovat jakýmkoliv způsobem rasismus či nacismus, ať už jménem provincie, regenta, či zprávami v poště.
8 / 50
3. Grafika 3.1. Rozvržení a obsah stránek Obecně jsou všechny stránky tvořeny ve stylu Hlavička - Tělo - Zápatí. Jednotlivé části se mění a jsou stejné vždy pro jeden typ stránek - vytváření účtu, přihlašování, nebo samotná hra. Barvy jsem volil tmavé, protože se mi líbí a obsah mi pak připadá více ucelený a též protože to bylo uvedeno již v zadání práce. Tlačítka hlavních akcí jsem vytvořil grafická, jelikož to lahodí oku, zvýší kontrast a zlepší orientaci na stránkách.
3.1.1 Index Zde je na místě hlavičky logo hry, které je na okrajích do ztracena (působí nenásilně, zapadá do pozadí) a ústředním motivem je středověký zdobený meč s odleskem.
Obr. 1: Logo hry Jako tělo se zobrazí dva obrázky s grafickými tlačítky umožňujícími výběr registrace, nebo přihlášení. Registrace je reprezentována obrázkem pergamenu s brkem a přihlášení pak svazkem starých rezavých klíčů.
9 / 50
Obr. 2: Regsitrace a přihlášení
3.1.2 Vytváření účtu Hlavička je stejná jako na indexu a zůstává po celý průběh vytváření účtu a registrace. Na místě těla se zobrazuje postupně průběh vytváření účtu a informace o stavu registrace.
Obr. 3: Registrační formulář
3.1.3 Přihlašování Jako hlavičká zůstává logo a namísto registrace jsou v těle dvě formulářová pole k zadání údajů pro přihlášení (jméno, heslo) a grafické odesílací tlačítko. 10 / 50
Obr. 4: Přihlašovací formulář
3.1.4 Vlastní hra V hlavičce se nachází upravené logo, grafické pozadí, údaje o hráči (jméno, název provincie a rasa) a grafické tlačítko na odhlášení.
Obr. 5: Hlavička vlastní hry (zmenšeno) Tělo je tvořeno informaci o množství surovin hráčovy provincie, textovým menu s malými ikonami a pod ním obsahem dle vybrané akce z menu povětšinou tabulky a formulářová pole pro ovládání hry.
Obr. 6: Menu pro výběr akce
11 / 50
Obr. 7: Tabulka informací o provincii
3.1.5 Zápatí Vyskytuje se na všech stránkách a obsahuje odkazy na vytvoření účtu, přihlášení, pravidla a návod ke hraní. Dále je zde uveden autor, název hry a vyhrazení práv na používání díla a jeho částí.
Obr. 8: Zápatí stránek
12 / 50
3.2. CSS 3.2.1 Obecně Obecně jsem se snažil řešit vše relativním pozicováním (s absolutním pozicováním jsou mnohdy problémy), obrázky pak pozadím v divu nebo tabulce a veškeré úpravy textu přes css. To mi umožnilo pružně měnit vzhled celých stránek, aniž bych musel vše několikrát přepisovat na různých stránkách. Vytvořil jsem si též několik tlačítek přes css - pozadí a text se mění údálostí hover. Styl pro zmíněné tlačítko: .utok_vypis .utok_vypis:hover
3.2.2 „Matrjoška“ Vzhledem k designu a především rozvržení stránek, použil jsem tuto metodu, pomocí níž jsem vytvořil jakýsi „obal“, který vše drží pohromadě. Navíc pomáhá při odlaďování pro IE, protože používá jiný model zobrazení, který počítá pro šířku i vlastnosti objektu - padding a border. Úsek kódu s použitím této metody: ...
4. Vnitřní fungování hry & kódování 4.1. Struktury tabulek Zde jsou uvedeny jednotlivé tabulky a stručně vysvětlen význam jednotlivých polí.
4.1.1 Tabulka 'Provincie' Obsahuje informace o hráči (stavu jeho surovin, zbývající volné tahy, síla provincie apod.).
Obr. 9: Struktura tabulky 'Provincie'
15 / 50
id: unikátní číslo každého hráče - klíč tabulky, automaticky inkrementovaný player_name: jméno hráče player_pass: heslo hráče provincie_name: název hráčovy provincie active: zdali je účet aktivovaný (přes e-mail), ANO=1, NE=0 activate_link: obsahuje unikátní generovaný aktivační klíč pro aktivaci přes email (více viz 4.2.1 Vytvoření účtu) race: rasa hráče turns_done: počet odehraných tahů turns_left: volné zbývající tahy room: půda k dispozici gold, iron, stone, wood: aktuální stav surovin (zlato, železo, kámen, dřevo) mana, population: aktuální stav many a velikosti populace strength: síla provincie buildingID: ID právě stavěné budovy buildingCount: počet právě stavěných budov builtTurns: kolik tahů již byla daná stavba stavěna recrutingID: ID právě rekrutované jednotky recrutingCount: počet právě rekrutovaných jednotek recrutedTurns: pro vícetahové rekrutování jednotek - obdobně jako builtTurns last_action: pro přidávání tahů - údaj o poslední akci hráče ms_overflow: pro přidávání tahů - infomace o necelém tahu
4.1.2 Tabulka 'Posts' Obsahuje posílanou poštu mezi jednotlivými hráči ve hře.
Obr. 10: Struktura tabulky 'Posts' 16 / 50
id: unikátní číslo každé zprávy IDfrom: od koho je zpráva obdržena IDto: komu je zpráva určena posted_on: kdy byla zpráva poslána not_read: slouží k detekci nové pošty, přečteno=0, nová zpráva=1 body: tělo vlastní zprávy 4.1.3 Tabulka 'Buildings_Init' Obsahuje údaje o budovách - jejich ceny, náročnost na údržbu, potřebné suroviny, rasu, název budovy, kolik tahů trvá ji postavit atp. Je to 1 / 2 tabulek potřebných pro budovy.
Obr. 11: Struktura tabulky 'Buildings_Init' 17 / 50
bid: unikátní číslo každé budovy req_race: potřebná rasa name: název budovy req_gold, req_mana, req_population, req_wood, req_stone, req_iron, req_room: potřebné hodnoty (zlato, mana, populace, dřevo, kámen, železo, půda) req_turns: počet tahů potřebný ke stavbě jedné budovy req_bid: když je budová závislá na jiné budově, obsahuje její ID req_bcount: když je budová závislá na jiné budově, obsahuje počet potřebných budov, na kterých je závislá na jednu budovu up_gold, up_mana ... : suroviny, které jedna budova spotřebuje za jeden tah give_gold, give_mana ... : suroviny, které jedna budova přidá za jeden tah
4.1.4 Tabulka 'Buildings' Relační tabulka, obsahuje závislosti mezi hráči a budovami - vyjadřuje, kdo kterou budovu vlastní a kolikrát. Je to 2 / 2 tabulek potřebných pro budovy.
Obr. 12: Struktura tabulky 'Buildings' id: unikátní číslo každého záznamu v tabulce bid: ID budovy ownerid: ID vlastníka budovy count: počet budov name: název budovy
18 / 50
4.1.5 Tabulka 'Units_Init' Obsahuje údaje o jednotkách - jejich ceny, náročnost na údržbu, potřebné suroviny, rasu, název jednotky, kolik tahů trvá ji narekrutovat atp. Je to 1 / 2 tabulek potřebných pro jednotky.
Obr. 13: Struktura tabulky 'Units_Init' idu: unikátní číslo každé jednotky req_race: potřebná rasa type: název jednotky req_gold, req_mana, req_pop, req_wood, req_stone, req_iron: potřebné hodnoty (zlato, mana, populace, dřevo, kámen, železo) req_turns: počet tahů potřebný k narekrutování jedné jednotky req_buildingId: obsahuje ID budovy, kterou je nutno postavit, aby mohla být jednotka narekrutována 19 / 50
up_gold, up_mana ... : suroviny, které spotřebuje jedna jednotka za jeden tah att, def, ini, hp: parametry jednotek (síla, obrana, iniciativa, životy) fly: zdali je jednotka letecká=1, pozemní=0 shooter: zdali je jednotka střelecká=1, bojová=0 weapon_id_can_handle: ID výzbroje, kterou může daná jednotka nosit
4.1.6 Tabulka 'Units' Relační tabulka, obsahuje závislosti mezi hráči a jednotkami - vyjadřuje, kdo kterou jednotku vlastní a kolikrát. Je to 2 / 2 tabulek potřebných pro jednotky.
Obr. 14: Struktura tabulky 'Units' id: unikátní číslo každého záznamu v tabulce idu: ID jednotky ownerid: ID vlastníka jednotky count: počet jednotek eq_count: počet ozbrojených jednotek name: název jednotky
4.1.7 Tabulka 'Equip_Init' Obsahuje údaje o výzbroji - jejich cenu, potřebné suroviny, rasu, název výzbroje, kolik tahů trvá ji vyrobit atp. Je to 1 / 2 tabulek potřebných pro výzbroj.
20 / 50
Obr. 15: Struktura tabulky 'Equip_Init' eid: unikátní číslo každé výzbroje name: název výzbroje req_turns: počet tahů potřebný k vyrobení jedné výzbroje m_att, m_def ... : modifikátory vlastností jednotek (+útok, +obrana atd.).
4.1.8 Tabulka 'Equip' Relační tabulka, obsahuje závislosti mezi hráči a výzbrojí - vyjadřuje, kdo má kterou výzbroj ve zbrojnici a kolikrát. Je to 2 / 2 tabulek potřebných pro výzbroj.
Obr. 16: Struktura tabulky 'Equip' id: unikátní číslo každého záznamu v tabulce eid: ID výzbroje ownerid: ID vlastníka výzbroje count: počet kusů výzbroje, které má vlastník ve zbrojnici k dispozici name: název výzbroje
21 / 50
4.2. Obecná funkce 4.2.1 Vytvoření účtu Po vyplnění všech polí se odešlou informace na skript zpracuj.php, kde se zkontroluje zda jsou pole jméno, název provincie, e-mail vyplněny, že hráč souhlasí s pravidly hry a zda jsou pole heslo1 a heslo2 stejné a vyplněné. Pokud ano, následuje kontrola, zda jsou použité názvy volné (jestli daná osoba není registrovaná a nikdo nevlastní provincii se stejným názvem). Když je vše v pořádku, skript pošle na zadanou adresu e-mail s informacemi o registraci a přesměruje na skript registered.php, kde se zobrazí informace o úspěšné registraci. Úsek kódu, který obstará zaslání e-mailu: //Email s pokyny k aktivaci $message="Dobry den, vas ucet byl uspesne vytvoren. "; $message.="Nyni prosim kliknete na nasledujici odkaz pro aktivaci vaseho uctu. "; $message.="http://ages.mysteria.cz/registered.php?activate=$active_link_str "; mail($email,'Aktivace uctu na ',$message,"ContentType: text/html; charset=iso-8859-1");
Po kliknutí na aktivační odkaz v e-mailu, je hráč přesměrován na skript registered.php, kde se zobrazí informace, zda je daný účet aktivovaný a registrace je tak dokončena. Nabídne též rovnou odkaz k přihlášení do hry. Aktivační klíč vytvářím z ID hráče, jeho přezdívky a názvu jeho provincie. ID hráče je unikátní, protože je nastavené jako klíč tabulky provincie s parametrem auto_increment a proto celý aktivační klíč je též unikátní pro každého registrovaného hráče.
22 / 50
4.2.2 Přihlášení Po zadání jména a hesla se zkontroluje správnost přihlašovacích údajů (jméno odpovídá heslu) na skriptu zpracuj.php a když jsou údaje v pořádku, přesměruje již do hry na stránku design.php, která v sobě zahrnuje i skript pro ovládání hry main.php. Přihlášení kontroluji přes proměnnou typu SESSION. Všechna hesla jsou zakódována standardní funkcí PHP jednosměrným šifrováním MD5, jelikož to zvyšuje bezpečnost. Příklad kontroly přihlášení: if($_SESSION['logged']==1){ ...kód... }else{ header("location: login.php?err=3"); }
4.2.3 Stavění budov Tato sekce obsahuje tabulku s informacemi o budovách, které je možno stavět, nebo jsou již postaveny. Pod ní jsou buď formulářová pole na výběr stavěné budovy a na žádaný počet postavených budov s tlačítkem na odeslání příkazu ke stavění (na skript zpracuj.php), nebo výpis informací o průběhu stavění budov. Pro výběr budovy používám dynamicky generovanou roletku (formulářové pole select), jenž vytvářím ze záznamů v tabulce a vypočteného počtu možných budov ke stavění. Názvy jednotlivých budov, které je možno postavit, ukládám do dvou polí spolu s jejich bID. Při zadání stavby (tlačítkem postavit) se informace zašlou na skript zpracuj.php, kde se zkontroluje, zda je dostatek surovin na stavbu daných budov a když je, odečte suroviny za stavbu a uloží do databáze pokyny ke stavění (pole buildingID a buildingCount). Kolik která budova trvá tahů vychází z počtu postavených dílen. Některé budovy jsou závislé na jiných budovách, tzn. je potřeba mít postaven určitý počet budov, na kterých je budova závislá, abych mohl postavit jednu budovu požadovaného typu.
23 / 50
Roletka na stavění: echo "";
4.2.4 Bourání budov Sekce obsahuje tabulku s informacemi o již postavených budovách, které je možno zbourat. Je zde vypsáno, kolik daná budova spotřebuje surovin za tah a kolik surovin naopak přidá. Hráč tak získá lepší přehled o hospodaření se surovinami a usnadní pak výběr budov ke zbourání. Pro lepší orientaci jsem napsal funkci, která nahradí znak '0' za libovolný znak předaný jako parametr. Místo nul se tak zobrazí např. '-' a je tak jasně vidět, jaké suroviny budova spotřebovává. Pod tabulkou jsou formulářová pole k výběru typu budovy ke zbourání a počtu bouraných budov. Pro výběr bourané budovy používám dynamicky generovanou roletku (formulářové pole select), jenž vytvářím ze záznamů v tabulce a počtu možných budov ke zbourání, což je počet aktuálně postavených budov daného typu. Názvy jednotlivých budov, které je možno zbourat, ukládám do dvou polí spolu s jejich bID, stejně jako u stavění. Příkazy se odesílají na skript zpracuj.php, jsou zpracovány a následuje přesměrování zpět do sekce bourání budov. Za zbourání jakékoliv budovy nedostane hráč žádné suroviny zpět. Funkce ReplaceZeros - záměna '0' za požadovaný znak: //-------------------------------------------------------------------/* */function replaceZeros($char) //-------------------------------------------------------------------{ global $up_gold,$up_wood,$up_stone,$up_iron,$up_mana,$up_pop; global $give_gold,$give_wood,$give_stone,$give_iron,$give_mana, $give_pop; global $req_gold,$req_mana,$req_pop,$req_wood,$req_stone,$req_iron, $req_building_id;
4.2.5 Armáda Sekce obsahuje dvě tabulky - jedna s informacemi o jednotkách, které má hráč ve své armádě a druhá s informacemi o jednotkách, které hráč může narekrutovat do své armády. Je zde přehled, kolik jednotek daného typu vlastním, kolik jednotky spotřebují surovin za tah, jakého jsou typu, jakou mají sílu a kolik síly má hráč v daném typu jednotek. Pod první tabulkou jsou formulářová pole pro propouštění jednotek a pod druhou na rekrutování. Pro výběr jednotky používám dynamicky generovanou roletku (formulářové pole select), jenž vytvářím z možných jednotek k rekrutování, což zjišťuji z počtu již postavených rekrutovacích budov. Opět se příkazy odesílají na skript zpracuj.php, jsou zpracovány a následuje přesměrování zpět do sekce armády. Hráč může jednotky rekrutovat a propouštět. Propustit může libovolný počet jednotek za tah, ale rekrutovat může jen tolik, kolik mu dovolí postavené budovy odpovídajícího typu. Armáda je nejdůležitější prvek ve hře, respektive její složení, protože veškerá konfrontace s ostatními hráči vede přes ni.
25 / 50
4.2.6 Zbrojnice Sekce obsahuje dvě tabulky - jedna s počty výzbroje ve zbrojnici, kterou je možno přiřadit odpovídajícím jednotkám (názvy odpovádajících jednotek jsou též vypsány) a druhá s počty výzbroje již přiřazené jednotkám. U obou tabulek je uveden i počet, kolik ještě může hráč jednotek odzbrojit či vyzbrojit. Při propouštění se nejdříve propustí neozbrojené jednotky a až pak jednotky ozbrojené. Pod oběma tabulkami se nachází formulářová pole k přiřazování a odebírání výzbroje. Pro výběr výzbroje používám dynamicky generovanou roletku (formulářové pole select), jež vytvářím z počtu výzbroje daného typu v hráčově zbrojnici, nebo počtu výzbroje již přiřazené odpovídajícím jednotkám a ID dané výzbroje. Tyto informace ukládám do dvou polí a následně vypisuji jako prvky formulářového pole select. Příkazy se odesílají na skript zpracuj.php, jsou zpracovány a následuje přesměrování zpět do sekce zbrojnice. Ozbrojené jednotky mají své vlastnosti upravené dle modifikátorů jednotlivé výzbroje, viz 4.1.7 Tabulka 'Equip_Init'. Tím je možno armádu vylepšovat.
4.2.7 Pošta Sekce obsahuje menu s položkami nová pošta, všechna pošta a poslat zprávu. Hráč je po vybrání podsekce přesměrován na ten samý skript, s jinou kontrolní proměnnou. Ta zajistí výběr požadované podsekce. Nová pošta zobrazí se jen dosud nepřečtená pošta, všechna pošta - zobrazí se vše a po rozkliknutí podsekce poslat zprávu se zobrazí tabulka s výpisem všech provincíí s možností poslat zprávu. Po vyplnění formuláře je hráč přesměrován na skript zpracuj.php, kde se vyhodnotí zaslané informace a případně vloží nový záznam do tabulky Posts. Následující skript obstarává zaslání pošty (zpracuj.php): //-------------------------------------------------------------------//--- Posli zpravu --------------------------------------------------//-------------------------------------------------------------------if($action=='sendmail'){ Connect(); $idto=$_POST['idto']; $idfrom=$_POST['idfrom'];
Jeho funkční ekvivalent pro fight.php: function sendmail($idto,$idfrom,$body){ global $connection; if($idto>0){ $q=mysql_query("INSERT INTO posts VALUES('','$idfrom','$idto',NOW(),'1','$body')",$connection); return mysql_affected_rows($connection); }else{ return 0; } }
4.2.8 Provincie Sekce obsahuje výpis provincií, na které je možno útočit. Provincie jsou v rozmezí síly ±25% od stávající síly hráčovy provincie (tzn. 75% až 125% stávající síly). Toto omezení jsem zavedl pro lepší hratelnost a férovost hry, navíc útok na provincii se sílou dvakrát větší, než má hráč, se rovná sebevraždě.
27 / 50
4.3. Bojový systém 4.3.1 Popis Pro bojový systém jsem zvolil práci s třídami. Ty umožňují, dle mého názoru, nejkratši kód a efektivní práci s proměnnými. Každá jednotka je tak zvlášť a jsou pro ni načteny její vlastnosti - útok, obrana, přítel / nepřítel atd. Zde je ukázka deklarace třídy Stack (jednotky jednoho typu): //----TRIDA STACK----------------------------------------------------class stack { var $att,$def,$ini,$hp,$fly,$shooter,$name; var $eq_count; var $id,$idu; var $used_this_turn,$enemy; var $m_att,$m_def,$m_ini,$m_hp,$m_fly,$m_shooter; function stack($id,$idu,$count,$eq_count,$enemy){ $this->id=$id; $this->idu=$idu; $this->eq_count=$eq_count; $this->count=$count; $this->enemy=$enemy; $this->used_this_turn=0; } function getStackStats(){ $idu=$this->idu; $sq=select("att,def,ini,hp,fly,shooter,type","units_init","idu=$ idu"); list($att,$def,$ini,$hp,$fly,$shooter,$name)=fetch($sq); $this->att=$att; $this->def=$def; $this->ini=$ini+$shooter*1000; $this->hp=$hp; $this->fly=$fly; $this->shooter=$shooter; $this->name=$name; } function getStackEqStats(){ $eid=getEqIdToUnit($this->id); $eq=select("m_att,m_def,m_ini,m_hp,m_fly,m_shooter","equip_init" ,"eid=$eid LIMIT 1"); if($eq>0){ while( list($m_att,$m_def,$m_ini,$m_hp,$m_fly,$m_shooter)=fetch($eq) ){ $this->m_att=$m_att; $this->m_def=$m_def; $this->m_ini=$m_ini; $this->m_hp=$m_hp;
28 / 50
$this->m_fly=$m_fly; $this->m_shooter=$m_shooter;
} }
}
function setCount($count){ $this->count=$count; } function resetTurn(){ $this->used_this_turn=$used_this_turn; } } //----KONEC TRIDY STACK---------------------------------------------
Jak je vidno z ukázky, používám funkce deklarované pro třidu Stack, pro nastavení vlastností, počtu, jestli již útočil, zkrátka veškeré operace s objekty třídy Stack řeším přes funkce.
4.3.2 Diagram Pro větší přehlednost a lepší pochopení přikládám místo slovního popisu obecný diagram bojového systému, ze kterého by mělo být fungování jasné.
29 / 50
Obr. 17: Diagram bojového systému
30 / 50
4.4. Funkce Zde popíšu několik užitečných a často používaných funkcí, které jsem napsal, abych si ulehčil práci a zpřehlednil si kód.
4.4.1 Connect Velice užitečná funkce, volám ji mnohokrát, dokonce snad na každé stránce. Jak už vyplývá z názvu, slouží k připojení k MySQL databázi, navíc nastaví UTF-8 komunikaci mezi skriptem a databází. Funkce nemá žádný parametr. Pokud se nepodaří vytvořit spojení, přeruší provádění skriptu přikazem die a vypíše chybovou hlášku, jinak vybere konkrétní databázi na předloženém serveru. //-------------------------------------------------------------------/* */function Connect(){ //-------------------------------------------------------------------$con_server="mysql.webzdarma.cz"; $con_name="ages16"; $con_db="ages16"; $con_pass="!123654"; global $connection; $connection=mysql_connect($con_server,$con_name,$con_pass); @mysql_query("SET NAMES UTF8"); if(!$connection){ die("Error. ".mysql_error() ); }else{ mysql_select_db($con_db,$connection); } return 1; }
4.4.2 Select Velmi užitečná funkce, slouží k výběru dat z tabulky pomocí MySQL příkazu SELECT. Je zde možnost tzv. debugu, což umožňuje vypsat příkaz vykonávaný databází. To se moc hodí při odlaďování. Funkce má jako parametr tři proměnné - str1, str2 a str3. Jejich význam je sloupec, tabulka, podmínka. Jak vidno, značně to ulehčí práci při psaní příkazů typu SELECT. Funkce vrací výsledek dotazu na MySQL databázi.
31 / 50
//-------------------------------------------------------------------/* */function select($str1,$str2,$str3) //-------------------------------------------------------------------{ global $connection; global $debug; if($debug==1){ echo " SELECT $str1 FROM $str2 WHERE $str3 "; } return mysql_query("SELECT $str1 FROM $str2 WHERE $str3",$connection); }
4.4.3 TableUpper & TableLower Tyto dvě funkce používám k vytvoření grafických okrajů kolem kódu obsaženém v nich. Tvoří je jen PHP příkazy echo, které vytvoří tabulku, kde jsou na pozadí jednotlivých divů obrázky tenkých linek. Vytvoří se tak grafický obal daného obsahu. //-------------------------------------------------------------------/* */function tableUpper(){ //-------------------------------------------------------------------echo("
4.4.4 TailMenu Tuto funkci vkládám (volám) na konci každé stránky a dělá to, že zobrazí ocas stránky - menu, ve kterém je na výběr odkaz na registraci, přihlášení, pravidla hry a návod ke hraní. //-------------------------------------------------------------------/* */function tailMenu(){ //-------------------------------------------------------------------echo "
5. Závěr S prací jsem spokojen a myslím si, že za tak krátký časový úsek jsem vytvořil vcelku komplexní a hlavně hratelnou hru. Hra je přístupná na adrese http://www.ages.mysteria.cz. Práce na tomto projektu mi přinesla mnoho nových poznatků a zkušeností, které bych jinak možná nezískal a navíc jsem se v průběhu docela bavil. Vybral jsem si téma, které je mi blízké, a tak šla práce vcelku od ruky. Během práce jsem narazil na několik menších i závažnějších problémů, které jsem ale vždy po chvíli přemýšlení byl schopen překonat vlastními prostředky. Zabralo to mnoho času, ale nelituji té ztráty, již teď vidím, že se to vyplatilo. Získal jsem dobrou znalost PHP programování, CSS stylování a práce s MySQL databází, což mi může být v budoucnu velice prospěšné. Také bych rád uplatnil získané znalosti na podobný projekt, ale nechal bych si více času na vývoj a propracování jednotlivých částí.
7. Přílohy Kvůli náročnosti tisku jsou zde rozepsány jen některé skripty. Vybral jsem stěžejní část, aby bylo možno alespoň rámcově práci prohlédnout a bylo jasné, že vím, co dělám. Všechny skripty jsou k dispozici na přiloženém CD-ROM.
$error=0; if ($nick==''){ $error=1; } if ($prov_name==''){ $error=1; } if ($email==''){ $error=1; } if ($pass1==''){ $error=1; } if ($pass1<>$pass2){ $error=2; } if (!($souhlas=='on')){ $error=5; } Connect(); //je jmeno hrace volne? $q=mysql_query("SELECT id FROM provincie WHERE player_name='$nick'",$connection); while(list($id)=mysql_fetch_row($q) ){ $error=3; } //je jmeno provincie volne? $q=mysql_query("SELECT id FROM provincie WHERE provincie_name='$prov_name'",$connection); while(list($id)=mysql_fetch_row($q) ){ $error=4; } if($error>0){ header("location: register.php?error=".$error."&nick=".$nick."&prov_name=".$prov_name."&email=".$emai l); }else{ $pass_str=MD5($pass1); //Nove upravy provincie resit zde $q=mysql_query("INSERT INTO provincie VALUES('','$nick','$pass_str','$prov_name','0','insert_a_link','$race','0','500','1 00','10000','10000','10000','10000','10000','2000','0','0','0','0','0','0','0','0', '0')",$connection); //Generovani unikatniho aktivacniho kodu $q=mysql_query("SELECT id FROM provincie WHERE player_name='$nick' LIMIT 1",$connection); list($id)=mysql_fetch_row($q); $active_link=$id+$nick+$prov_name; $active_link_str=MD5($active_link); //Ulozeni.. $q=mysql_query("UPDATE provincie SET activate_link='$active_link_str' WHERE id='$id' and active='0' LIMIT 1"); //Email s pokyny k aktivaci $message="Dobry den, vas ucet byl uspesne vytvoren. "; $message.="Nyni prosim kliknete na nasledujici odkaz pro aktivaci vaseho uctu. "; $message.="http://ages .mysteria.cz/registered.php?activate=$active_link_str"; mail($email,'Aktivace uctu na ',$message,"Content-Type: text/html; charset=iso-8859-1"); $error=mysql_error(); if($error==0){ header("location: registered.php"); } } mysql_close($connection); } //----------------------------------------------------------------------------//----Login -------------------------------------------------------------------
40 / 50
//----------------------------------------------------------------------------if($action==2){ $nick=$_REQUEST['nick']; $nick=htmlspecialchars($nick); $pass=$_REQUEST['pass']; if ($nick==''){ header("location: login.php?err=1&nick=$nick"); } $pass_str=MD5($pass); Connect(); $q=mysql_query("SELECT id,player_name FROM provincie WHERE player_name='".$nick."' AND player_pass='".$pass_str."' AND active='1' LIMIT 1"); list($id,$player_name)=mysql_fetch_row($q); //Byl nalezen uzivatel s odpovidajicimi udaji? if(mysql_num_rows($q)>0){ $_SESSION['logged']=1; //Prihlas ho $_SESSION['loggedID']=$id; $_SESSION['loggedNAME']=$player_name; mysql_close($connection); header("location: design.php"); }else{ mysql_close($connection); header("location: login.php?err=1&nick=$nick"); } } //----------------------------------------------------------------------------//----LogOut ------------------------------------------------------------------//----------------------------------------------------------------------------if($action==3){ unset($_SESSION['logged']); unset($_SESSION['loggedID']); unset($_SESSION['loggedNAME']); header("location: login.php?err=2"); } //----------------------------------------------------------------------------//----Chci stavet $building_id------------------------------------------------//----------------------------------------------------------------------------if($action==5){ //checkPlayer(); $b_id=$_POST['bid']; $b_count=$_POST['bcount']; //Abych nemohl stavet 0 a -1 budov if($b_count>0){ Connect(); $stats_q=selectp("turns_left,gold,mana,population,iron,stone,wood,room","id='$p _id'",$connection); list($p_turns_left,$p_gold,$p_mana,$p_population,$p_iron,$p_stone,$p_wood,$p_ro om)=mysql_fetch_row($stats_q); //load info o stavene budove $req_query=select("req_gold,req_mana,req_population,req_wood,req_stone,req_iron ,req_room,req_bid,req_bcount","buildings_init","bid=$b_id"); list($req_gold,$req_mana,$req_population,$req_wood,$req_stone,$req_iron,$req_
41 / 50
room,$req_bid,$req_bcount)=mysql_fetch_row($req_query); $passed=1; if($p_gold<($b_count*$req_gold) ){ $passed=0; echo "1";} if($p_mana<($b_count*$req_mana) ){ $passed=0; echo "2";} if($p_population<($b_count*$req_population) ){ $passed=0; echo "3";} if($p_wood<($b_count*$req_wood) ){ $passed=0; echo "4 $p_wood<($b_count*$req_wood)";} if($p_stone<($b_count*$req_stone) ){ $passed=0; echo "5";} if($p_iron<($b_count*$req_iron) ){ $passed=0; echo "6";} if($p_room<($b_count*$req_room) ){ $passed=0; echo "7";} if(!$passed){ Close(); header("location: design.php?action=1&msg=2"); }else{ //Uber penize za stavby ( na zacatku ) $p_curr_gold=$p_gold-($b_count*$req_gold); $p_curr_mana=$p_mana-($b_count*$req_mana); $p_curr_pop=$p_population-($b_count*$req_population); $p_curr_iron=$p_iron-($b_count*$req_iron); $p_curr_stone=$p_stone-($b_count*$req_stone); $p_curr_wood=$p_wood-($b_count*$req_wood); $p_curr_room=$p_room-($b_count*$req_room); $update_player_data_str="gold='$p_curr_gold',mana='$p_curr_mana',population=' $p_curr_pop',iron='$p_curr_iron',stone='$p_curr_stone',wood='$p_curr_wood',room='$p _curr_room'"; $update_player_data_q=mysql_query("UPDATE provincie SET $update_player_data_str WHERE id='$p_id' LIMIT 1",$connection); $q=mysql_query("UPDATE provincie SET buildingID='$b_id',buildingCount='$b_count' WHERE id=$p_id LIMIT 1",$connection); Close(); header("location: design.php?action=1&msg=3"); } }else{ //Pocet staveb < 1 header("location: design.php?action=1&msg=4"); } } //----------------------------------------------------------------------------//--- Cancel building --------------------------------------------------------//----------------------------------------------------------------------------if($action==6){ //checkPlayer(); Connect(); $q=select("buildingID,buildingCount","provincie","id=$p_id LIMIT 1"); list($b_id,$b_count)=fetch($q); //Load player data $stats_q=selectp("turns_left,gold,mana,population,iron,stone,wood,room","id='$p_i d'",$connection); list($p_turns_left,$p_gold,$p_mana,$p_pop,$p_iron,$p_stone,$p_wood,$p_room)=mys ql_fetch_row($stats_q); //load info o drive stavene budove $req_query=select("req_gold,req_mana,req_population,req_wood,req_stone,req_iron,r eq_room,req_bid,req_bcount","buildings_init","bid=$b_id");
42 / 50
list($req_gold,$req_mana,$req_pop,$req_wood,$req_stone,$req_iron,$req_room,$r eq_bid,$req_bcount)=mysql_fetch_row($req_query); $p_curr_gold=$p_gold+(0.25*$b_count*$req_gold); $p_curr_mana=$p_mana+(0.25*$b_count*$req_mana); $p_curr_pop=$p_pop+(0.25*$b_count*$req_pop); $p_curr_iron=$p_iron+(0.25*$b_count*$req_iron); $p_curr_stone=$p_stone+(0.25*$b_count*$req_stone); $p_curr_wood=$p_wood+(0.25*$b_count*$req_wood); $p_curr_room=$p_room+($b_count*$req_room); $update_player_data_str="gold='$p_curr_gold',mana='$p_curr_mana',population='$p_c urr_pop',iron='$p_curr_iron',stone='$p_curr_stone',wood='$p_curr_wood',room='$p_cur r_room'"; $update_player_data_q=mysql_query("UPDATE provincie SET $update_player_data_str WHERE id='$p_id' LIMIT 1",$connection); $q=mysql_query("UPDATE provincie SET buildingID='0',buildingCount='0',builtTurns='0' WHERE id=$p_id LIMIT 1",$connection); header("location: design.php?action=1&msg=5"); } //----------------------------------------------------------------------------//--- Bourani ----------------------------------------------------------------//----------------------------------------------------------------------------if($action=='bourat'){ $b_id=$_POST['bid']; $b_destroy_count=$_POST['bcount']; Connect(); $q=select("count","buildings","bid='$b_id' AND ownerid='$p_id' LIMIT 1"); list($b_count)=fetch($q); $b_count-=$b_destroy_count; if($b_count>0){ $q=mysql_query("UPDATE buildings SET count='$b_count' WHERE bid=$b_id AND ownerid='$p_id' LIMIT 1",$connection); }else{ $q=mysql_query("DELETE FROM buildings WHERE bid=$b_id AND ownerid='$p_id' LIMIT 1",$connection); } $destroyed=$b_destroy_count; Close(); header("location: design.php?action=4&bdestroyed=$destroyed"); }
//----------------------------------------------------------------------------//--- Chci rekrutovat --------------------------------------------------------//----------------------------------------------------------------------------if($action=='rekrutovat'){ Connect(); $u_id=$_POST['uid']; $u_count=$_POST['ucount']; //Načtení ID potřebné budovy $req_building_id_q=select("req_buildingId","units_init","idu='$u_id'"); list($req_building_id)=fetch($req_building_id_q);
43 / 50
//Načtení počtu postavených budov potřebných k výrobě jednotky $req_buildings_owned_q=select("count","buildings","ownerid='$p_id' AND bid='$req_building_id'"); list($req_buildings_owned)=fetch($req_buildings_owned_q); echo $req_building_id."x".$req_buildings_owned; //Kdyz vlastnim aspon jednu budovu potrebnou na jednotku if($req_buildings_owned>0){ $q=mysql_query("UPDATE provincie SET recrutingID='$u_id',recrutingCount='$u_count' WHERE id='$p_id' LIMIT 1",$connection); Close(); //Ok header("location: design.php?action=2&msg=1"); }else{ Close(); //Neproslo header("location: design.php?action=2&msg=2"); } } //----------------------------------------------------------------------------//--- Cancel recruit ---------------------------------------------------------//----------------------------------------------------------------------------if($action=='zrusitrec'){ Connect(); $q=mysql_query("UPDATE provincie SET recrutingID='0',recrutingCount='0' WHERE id=$p_id LIMIT 1",$connection); Close(); header("location: design.php?action=2"); } //----------------------------------------------------------------------------//--- Propust jednotky -------------------------------------------------------//----------------------------------------------------------------------------if($action=='propustit'){ $u_id=$_POST['uid']; $u_dismiss_count=$_POST['ucount']; Connect(); $u_count=getUnitCount($u_id); $eq_count=getEqUnitCount($u_id); $u_count-=$u_dismiss_count; $new_eq_count=min($u_count,$eq_count); $new_eq_count=max($new_eq_count,0); if($u_count>0){ $q=mysql_query("UPDATE units SET count='$u_count', eq_count='$new_eq_count' WHERE idu=$u_id AND ownerid='$p_id' LIMIT 1",$connection); }else{ $q=mysql_query("DELETE FROM units WHERE idu=$u_id AND ownerid='$p_id' LIMIT 1",$connection); } Close(); header("location: design.php?action=2"); }
44 / 50
//----------------------------------------------------------------------------//--- Ozbroj jednotky --------------------------------------------------------//----------------------------------------------------------------------------if($action=='ozbrojit'){ Connect(); $eid=$_POST['eid']; $ecount=$_POST['ecount']; $ecount=max(0,$ecount); if( $ecount>0 ){ $e_owned=getEqCount($eid); $idu=getUnitIdToEq($eid); $u_count=getUnitCount($idu); $u_eq_count=getEqUnitCount($idu); $u_can_eq=$u_count-$u_eq_count; //Pokud mam dostatek vyzbroje a pokud jich tolik vubec muzu vyzbrojit if( ($e_owned>=$ecount)&&($ecount<=$u_can_eq) ){ //Pridej vyzbroj jednotkam $new_u_eq_count=$u_eq_count+$ecount; $q=mysql_query("UPDATE units SET eq_count='$new_u_eq_count' WHERE ownerid=$p_id AND idu=$idu",$connection); //Uber vyzbroj ve zbrojnici $new_e_owned=$e_owned-$ecount; if($new_e_owned>0){ $q=mysql_query("UPDATE equip SET count='$new_e_owned' WHERE ownerid=$p_id AND eid=$eid",$connection); }else{ $q=mysql_query("DELETE FROM equip WHERE ownerid=$p_id AND eid=$eid",$connection); } Close(); header("location: design.php?action=3&msg=ozbrojeno"); }else{ Close(); header("location: design.php?action=3&msg=nelze_ozbrojit"); } }else{ Close(); header("location: design.php?action=3&msg=nelze_ozbrojit"); } } //----------------------------------------------------------------------------//--- Odzbroj jednotky -------------------------------------------------------//----------------------------------------------------------------------------if($action=='odzbrojit'){ Connect(); $eid=$_POST['eid']; $ecount=$_POST['ecount']; $ecount=max(0,$ecount); if( $ecount>0 ){ $idu=getUnitIdToEq($eid); $eq_count=getEqUnitCount($idu);
45 / 50
if( $eq_count>=$ecount ){ $new_eq_count=$eq_count-$ecount; $q=mysql_query("UPDATE units SET eq_count='$new_eq_count' WHERE ownerid=$p_id AND idu=$idu",$connection); $already_have=getEqCount($eid); if( $already_have>0 ){ $new_p_eq_count=$already_have+$ecount; $q=mysql_query("UPDATE equip SET count='$new_p_eq_count' WHERE ownerid=$p_id AND eid=$eid",$connection); }else{ $eq_name=getEqName($eid); $q=mysql_query("INSERT INTO equip VALUES('','$eid','$p_id','$ecount','$eq_name')",$connection); } Close(); header("location: design.php?action=3&msg=odzbrojeno"); }else{ Close(); header("location: design.php?action=3&msg=nelze_odzbrojit"); } }else{ Close(); header("location: design.php?action=3&msg=nelze_odzbrojit"); } //post - kolik toho chci a co = eq-count, eq-id //zjistit, kolik mam ozbrojenejch a jesi teda muzu tolik pripsat do equip playerovi //pokud to projde, zapis do DB - --units.eq_count / ++equip.count //pokud ne, vyfakni } //----------------------------------------------------------------------------//--- Posli zpravu -----------------------------------------------------------//----------------------------------------------------------------------------if($action=='sendmail'){ Connect(); $idto=$_POST['idto']; $idfrom=$_POST['idfrom']; $body=$_POST['body']; if($idto>0){ $q=mysql_query("INSERT INTO posts VALUES('','$idfrom','$idto',NOW(),'1','".htmlspecialchars($body)."')",$connection); //if(mysql_affected_rows($connection)>0 ){ if($q>0){ header('location: design.php?action=5&msg=sent'); }else{ header('location: design.php?action=5&msg=error'); } }else{ header('location: design.php?action=5&msg=warning'); } } //----------------------------------------------------------------------------//--- Smaz zpravu ------------------------------------------------------------//----------------------------------------------------------------------------if($action=='deletemail'){ Connect(); $id=$_REQUEST['id']; if($id>0){
46 / 50
$q=mysql_query("DELETE FROM posts WHERE id=$id",$connection); //if(mysql_affected_rows($connection)>0 ){ if($q<>0){ header('location: design.php?action=5&a=allmail&msg=deleted'); }else{ header('location: design.php?action=5&a=allmail&msg=error'); } }else{ header('location: design.php?action=5&a=allmail&msg=warning'); } }?>