W tej części serii „Tworzenie gry przeglądarkowej” damy graczowi możliwość atakowania innych graczy. Za zwycięstwa będzie otrzymywał punkty, a za porażki będzie je tracił. Punktów użyjemy przy tworzeniu rankingu. Zaczynajmy 🙂

Na początku utwórzmy nową tabelę o nazwie report. Będzie ona przechowywała raporty z walki. Dzięki temu będziemy w stanie uniknąć sytuacji, w której gracz atakowałby innego setki razy pod rząd. W dalszej części ustawimy co jaki czas gracz będzie mógł atakować.

tabela report

Tabela report składa się z czterech kolumn:

  • id – unikalny identyfikator raportu
  • attacker – numer id gracza, który atakuje (zgodny z pozostałymi tabelami)
  • defender – numer id gracza, który jest atakowany (zgodny z pozostałymi tabelami)
  • time – czas (uniksowy), po którym można ponownie zaatakować gracza

Możemy teraz przejść do klasy ModuleLoader. Dodamy nową metodę o nazwie loadUserList, która wyświetli listę graczy, których możemy zaatakować. Pod metodą loadItemsEquipment dodaj poniższy kod:

static public function loadUserList($uid, $username, $points) {
        echo '
            <li>'.$username.' ('.$points.' pkt.) <button type="button" class="attack" name="'.$uid.'">Zaatakuj</button></li>
        ';      
}

Jest to bardzo prosta metoda, więc przejdziemy od razu do jej zastosowania.

Zanim uzupełnimy case walka, przejdźmy na moment do klasy UserManager i dodajmy jedną prostą metodę:

public function getUsernameById($ID) { //pobieranie nazwy użytkownika mając jego id
            
            $array = DatabaseManager::selectBySQL("SELECT username FROM users WHERE id='".$ID."'");
            foreach($array as $user) {
                $username = $user['username'];
            }
            return $username; //zwracamy id
            
}

Ta metoda na podstawie przyjętego w parametrze numeru id zwróci nazwę użytkownika.

Możemy teraz wrócić do ModuleLoader i uzupełnić case walka:

case 'walka':
            echo '
                <section class="content walka">  	
                    <div class="container">        
                    	 <h2>Walka:</h2>         
                         
                         <div class="row">
                        <div class="col-xs-8 col-sm-6 col-md-4 col-lg-4 col-lg-offset-4 col-md-offset-4 col-sm-offset-3 col-xs-offset-2">
                         <ul class="user_list" id="u_'.$_SESSION['uid'].'">
                         ';
                            
            			$select_users = DatabaseManager::selectBySQL("SELECT * FROM stats WHERE id!='".$_SESSION['uid']."'");
                                 
                            if($select_users) {
                                foreach($select_users as $user) { 
                                 $username = UserManager::getUsernameById($user['id']);   
                                 ModuleLoader::loadUserList($user['id'], $username, $user['points']);
                                }
                            }	
            
                 echo ' </ul></div></div>
                  
                    </div>   
                </section>
            ';
break;

Powyższy kod wyświetla listę graczy, których możemy zaatakować. Warto zwrócić uwagę, że w zapytaniu SELECT podajemy warunek by id nie było równe id sesji, czyli identyfikatorowi zalogowanego gracza (nie chcemy by gracz mógł zaatakować sam siebie).

Dodamy jeszcze kilka reguł CSS, które poprawią położenie elementów. W pliku style.css dodaj następujące linie kodu:

.user_list {
    list-style: none;
}

.user_list li {
    text-align: left;
    margin-top: 25px;
    border-bottom: 2px solid black;
    font-size: 20px;
}

.user_list li button {
    float: right;
    position: relative;
    bottom: 3px;
    font-size: 16px;
}

Możemy teraz przejść do klasy GameManager by napisać metodę duel. Będzie ona obsługiwała cały pojedynek graczy.

public function duel($attacker_id, $defender_id) { 

                if($attacker_id == $defender_id) {
                    return false;
                    exit;
                }
     
                $select_report = DatabaseManager::selectBySQL("SELECT * FROM report WHERE attacker='".$attacker_id."' AND defender='".$defender_id."' AND time>'".time()."'"); //pobranie statystyk atakującego
     
                $select_attacker = DatabaseManager::selectBySQL("SELECT * FROM stats WHERE id='".$attacker_id."'"); //pobranie statystyk atakującego
                $select_defender = DatabaseManager::selectBySQL("SELECT * FROM stats WHERE id='".$defender_id."'"); //pobranie statystyk obrońcy
                
                $select_attacker_eq = DatabaseManager::selectBySQL("SELECT * FROM items WHERE uid='".$attacker_id."' AND is_equipped=1"); //pobranie ekwipunku atakującego
                $select_defender_eq = DatabaseManager::selectBySQL("SELECT * FROM items WHERE uid='".$defender_id."' AND is_equipped=1"); //pobranie ekwipunku obrońcy
                
                
                
                if(!$select_report){ //można zaatakować gracza
                
                if($select_attacker && $select_defender) { //znaleziono graczy w bazie
                 
                    
                    foreach($select_attacker as $arr) {
                        $attacker = $arr;
                    }     
                    foreach($select_defender as $arr2) {
                        $defender = $arr2;
                    }   
                    
                    
                    if($select_attacker_eq) { //dodanie statystyk z założonych przedmiotów atakującemu
                       foreach($select_attacker_eq as $attacker_eq) {
                            $attacker['attack'] += $attacker_eq['attack'];
                            $attacker['defense'] += $attacker_eq['defense'];
                        }   
                    }
                    
                    if($select_defender_eq) { //dodanie statystyk z założonych przedmiotów obrońcy
                       foreach($select_defender_eq as $defender_eq) {
                            $defender['attack'] += $defender_eq['attack'];
                            $defender['defense'] += $defender_eq['defense'];
                        }   
                    }
                                
                  
                    while($attacker['hp'] > 0 && $defender['hp'] > 0) { //pętla wykonująca się póki hp jednego z gracza nie spadnie poniżej 0
                        $defender['hp'] -= ($attacker['attack'] / ($defender['defense'] / 10)); //obliczenie obrażeń na obrońcy
                        $attacker['hp'] -= ($defender['attack'] / ($attacker['defense'] / 10)); //obliczenie obrażeń na atakującym           
                    }
                    
                    if($attacker['hp'] <= 0 && $defender['hp'] <= 0) { //remis
                        $time = time() + 3600;
                        $res = DatabaseManager::insertInto("report", Array('attacker' => ''.$attacker['id'].'', //dodanie raportu do bazy
                                                                        'defender' => ''.$defender['id'].'',
                                                                        'time' => ''.$time.''    
                                                                        ));                      
                        return 'remis';
                    }
                    
                    elseif($attacker['hp'] > 0 && $defender['hp'] <= 0) { //atakujący zwycięża
                        $update_points = $attacker['points'] + 10;             
                        DatabaseManager::updateTable("stats", Array('points' => ''.$update_points.''), Array('id' => ''.$attacker['id'].'')); //dodanie punktów
                        $time = time() + 3600;
                        $res = DatabaseManager::insertInto("report", Array('attacker' => ''.$attacker['id'].'', //dodanie raportu do bazy
                                                                        'defender' => ''.$defender['id'].'',
                                                                        'time' => ''.$time.''    
                                                                        ));     
                        return 'zwyciestwo';
                    }
                    
                    elseif($defender['hp'] > 0 && $attacker['hp'] <= 0) { //obrońca zwycięża
                        $update_points = $attacker['points'] - 5;             
                        DatabaseManager::updateTable("stats", Array('points' => ''.$update_points.''), Array('id' => ''.$attacker['id'].'')); //odjęcie punktów
                        $time = time() + 3600;
                        $res = DatabaseManager::insertInto("report", Array('attacker' => ''.$attacker['id'].'', &nbsp;//dodanie raportu do bazy
                                                                        'defender' => ''.$defender['id'].'',
                                                                        'time' => ''.$time.''    
                                                                        ));      
                        return 'porazka';
                    }
                  
                } //nie znaleziono graczy/gracza w bazie
                
                else {
                    return false;
                    exit;
                } 
                
                } else { //nie można zaatakować gracza
                    return false;
                }                                                                      
}

Ta metoda jest dosyć rozbudowana. Całość została opatrzona komentarzami, które tłumaczą co w danym momencie się dzieje.

Przejdźmy teraz do pliku wlasny.js i wystosujmy żądanie:

session_id = $('.user_list').attr('id'); //pobranie id

$('.attack').on('click', function() {
        var element = $(this);
        var defender_id = $(this).attr('name');
        
        var posting = $.post( 'battle/', { attacker: session_id, defender: defender_id } ); //wysłanie żądania z parametrami
       
        posting.done(function( data ) { //po zakończeniu żądania
            if(data.trim() == 'zwyciestwo'){ 
                $.smkAlert({text: 'Zwyciężyłeś. Zdobyto 10 punktów.', type:'success'}); //wyświetlenie komunikatu - zwycięstwo
            }
            else if(data.trim() == 'remis') {              
                $.smkAlert({text: 'Remis. Nie zdobyłeś żadnych punktów.', type:'success'}); //wyświetlenie komunikatu - remis
            }
            else if(data.trim() == 'porazka') {              
                $.smkAlert({text: 'Przegrałeś. Tracisz 5 punktów.', type:'danger'}); //wyświetlenie komunikatu - porażka
            }
            else {
                $.smkAlert({text: 'Nie możesz teraz zaatakować tego gracza.', type:'danger'}); //wyświetlenie komunikatu o niepowodzeniu
            }
      });    
});

Komunikaty są wyświetlane w zależności od odpowiedzi, którą zwraca metoda duel. Mając gotowe żądanie możemy utworzyć plik, do którego żądanie zostanie przesłane.

W folderze LIBRARY utwórzmy nowy plik o nazwie battle.library.php i uzupełnijmy go następującym kodem:

<?php

if(isset($_POST['attacker']) && isset($_POST['defender'])) {
    
   $um = new GameManager;
      
      $id = ltrim($_POST['attacker'], 'u_'); 
      
      $res = $um->duel($id, $_POST['defender']);
      
      if($res) {
          echo $res;
          exit;
      } else {     
          return 0;    
      }
 
    
} else {
   return false;
}

?>

Plik jest niemal identyczny jak poprzednie pliki, które tworzyliśmy w tym folderze. Tym razem zwraca on jednak odpowiedź z klasy GameManager.

Pamiętajmy jeszcze o dodaniu nowego case w klasie MainPage:

case 'battle':
     require_once $this->active_page.".library.php";
break;

Od teraz gracze mają możliwość walczenia ze sobą 🙂

Przykład możesz zobaczyć tutaj.

Paczkę możesz pobrać tutaj.

  • plastian

    Witam, mam problem. Na stronie walki oraz rankingu wampserver wywala mi komunikaty z błędami.
    Całą serię poradników klepałem kod ręcznie ale w sumie to mało istotne bo w końcu się poddałem i pobrałem finalną paczkę. Niestety się okazało, że to kompletnie nic nie zmieniło :/
    Ów komunikat: Strict standards: Non-static method UserManager::getUsernameById() should not be called statically in E:wamp64wwwgraCLASSModuleLoader.class.php on line 261
    Identyczny dla każdego konta w rankingu.
    Może mi ktoś wyjaśnić czemu skrypt (fragment kodu odpowiedzialny z ten błąd) jest napisany właśnie w taki sposób a nie inny co skutkuje wyświetlaniem tego błędu?