Documentation

Ui Examples Continued

Π­Ρ‚ΠΎ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π·Π΄Π΅Π»Π° с ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌΠΈ интСрфСйсов. ΠŸΠ΅Ρ€Π²ΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π½Π°ΠΉΡ‚ΠΈ Π² 4.ui-examples.md.

ΠœΠ΅Ρ‚ΠΎΠ΄Ρ‹ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΈΠ½Π²Π΅Π½Ρ‚Π°Ρ€Π΅ΠΌ (ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅)

ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅ класса Inventory с ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΈΠ½Π²Π΅Π½Ρ‚Π°Ρ€Π΅ΠΌ:

class Inventory {
  // ... (Π½Π°Ρ‡Π°Π»ΠΎ класса Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ Ρ„Π°ΠΉΠ»Π΅)
  
  renderInventory() {
    const grid = document.getElementById('inventoryGrid');
    grid.innerHTML = '';
    
    // Π€ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΡ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠ² ΠΏΠΎ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ Π²ΠΊΠ»Π°Π΄ΠΊΠ΅
    const filteredItems = this.currentFilter === 'all' 
      ? this.items 
      : this.items.filter(item => item.type === this.currentFilter);
    
    // Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ слотов для ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠ²
    for (let i = 0; i < 24; i++) {
      const slot = document.createElement('div');
      slot.className = 'wg-inventory-slot';
      slot.dataset.index = i;
      
      // Если Π² этом слотС Π΅ΡΡ‚ΡŒ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚
      const item = filteredItems.find(item => item.slotIndex === i);
      if (item) {
        slot.innerHTML = `
          <div class="wg-item" data-id="${item.id}" draggable="true">
            <img src="${item.icon}" alt="${item.name}">
            ${item.quantity > 1 ? `<span class="wg-item-quantity">${item.quantity}</span>` : ''}
            ${item.equipped ? '<span class="wg-item-equipped">E</span>' : ''}
          </div>
        `;
        
        // Настройка пСрСтаскивания
        this.setupDragAndDrop(slot.querySelector('.wg-item'));
      }
      
      grid.appendChild(slot);
    }
  }
  
  setupDragAndDrop(itemElement) {
    itemElement.addEventListener('dragstart', (e) => {
      this.draggedItem = this.items.find(item => item.id === itemElement.dataset.id);
      e.dataTransfer.setData('text/plain', itemElement.dataset.id);
      setTimeout(() => {
        itemElement.classList.add('dragging');
      }, 0);
    });
    
    itemElement.addEventListener('dragend', () => {
      itemElement.classList.remove('dragging');
      this.draggedItem = null;
    });
  }
  
  setupEventListeners() {
    // Настройка Π²ΠΊΠ»Π°Π΄ΠΎΠΊ
    document.querySelectorAll('.wg-tab-button').forEach(button => {
      button.addEventListener('click', () => {
        document.querySelector('.wg-tab-button.active').classList.remove('active');
        button.classList.add('active');
        this.currentFilter = button.dataset.tab;
        this.renderInventory();
      });
    });
    
    // Настройка слотов для ΠΏΡ€ΠΈΠ΅ΠΌΠ° пСрСтаскиваСмых ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠ²
    document.querySelectorAll('.wg-inventory-slot').forEach(slot => {
      slot.addEventListener('dragover', (e) => {
        e.preventDefault();
        slot.classList.add('drag-over');
      });
      
      slot.addEventListener('dragleave', () => {
        slot.classList.remove('drag-over');
      });
      
      slot.addEventListener('drop', async (e) => {
        e.preventDefault();
        slot.classList.remove('drag-over');
        
        if (this.draggedItem) {
          const targetIndex = parseInt(slot.dataset.index);
          
          // Π’Ρ‹Π·ΠΎΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π΄Π²ΠΈΠΆΠΊΠ° для пСрСмСщСния ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π°
          await engine.call('moveInventoryItem', {
            itemId: this.draggedItem.id,
            targetSlot: targetIndex
          });
        }
      });
      
      // Π’Ρ‹Π±ΠΎΡ€ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π° ΠΏΡ€ΠΈ ΠΊΠ»ΠΈΠΊΠ΅
      slot.addEventListener('click', () => {
        const itemElement = slot.querySelector('.wg-item');
        if (itemElement) {
          const itemId = itemElement.dataset.id;
          this.selectItem(itemId);
        }
      });
    });
    
    // Настройка ΠΊΠ½ΠΎΠΏΠΎΠΊ дСйствий
    document.getElementById('btnSort').addEventListener('click', () => this.sortItems());
    document.getElementById('btnStack').addEventListener('click', () => this.stackItems());
    document.getElementById('btnDrop').addEventListener('click', () => this.dropSelectedItem());
    document.getElementById('btnUse').addEventListener('click', () => this.useSelectedItem());
    document.getElementById('btnEquip').addEventListener('click', () => this.equipSelectedItem());
  }
  
  selectItem(itemId) {
    this.selectedItem = this.items.find(item => item.id === itemId);
    this.showItemDetails();
  }
  
  showItemDetails() {
    if (!this.selectedItem) return;
    
    document.getElementById('itemName').textContent = this.selectedItem.name;
    document.getElementById('itemType').textContent = this.selectedItem.type;
    document.getElementById('itemImage').src = this.selectedItem.icon;
    document.getElementById('itemDescription').textContent = this.selectedItem.description;
    
    // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ характСристик ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π°
    const statsContainer = document.getElementById('itemStats');
    statsContainer.innerHTML = '';
    
    if (this.selectedItem.stats) {
      Object.entries(this.selectedItem.stats).forEach(([key, value]) => {
        const statElement = document.createElement('div');
        statElement.className = 'wg-item-stat';
        statElement.innerHTML = `<span class="wg-stat-name">${key}:</span> <span class="wg-stat-value">${value}</span>`;
        statsContainer.appendChild(statElement);
      });
    }
    
    // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ/ΡΠΊΡ€Ρ‹Ρ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ Π² зависимости ΠΎΡ‚ Ρ‚ΠΈΠΏΠ° ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π°
    document.getElementById('btnUse').style.display = 
      ['consumable', 'potion', 'scroll'].includes(this.selectedItem.type) ? 'block' : 'none';
    
    document.getElementById('btnEquip').style.display = 
      ['weapon', 'armor', 'accessory'].includes(this.selectedItem.type) ? 'block' : 'none';
    
    document.getElementById('btnEquip').textContent = 
      this.selectedItem.equipped ? 'Π‘Π½ΡΡ‚ΡŒ' : 'Π­ΠΊΠΈΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ';
    
    // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ панСль Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ
    document.getElementById('itemDetails').classList.add('visible');
  }
  
  updateInventoryStats() {
    const totalWeight = this.items.reduce((sum, item) => sum + (item.weight || 0) * (item.quantity || 1), 0);
    const usedSlots = this.items.length;
    
    document.getElementById('currentWeight').textContent = totalWeight.toFixed(1);
    document.getElementById('usedSlots').textContent = usedSlots;
  }
  
  async sortItems() {
    await engine.call('sortInventory', { criteria: 'type' });
  }
  
  async stackItems() {
    await engine.call('stackInventoryItems');
  }
  
  async dropSelectedItem() {
    if (!this.selectedItem) return;
    
    if (confirm(`Π’Ρ‹ ΡƒΠ²Π΅Ρ€Π΅Π½Ρ‹, Ρ‡Ρ‚ΠΎ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ Π²Ρ‹Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ ${this.selectedItem.name}?`)) {
      await engine.call('dropInventoryItem', { itemId: this.selectedItem.id });
      this.selectedItem = null;
      document.getElementById('itemDetails').classList.remove('visible');
    }
  }
  
  async useSelectedItem() {
    if (!this.selectedItem) return;
    
    await engine.call('useInventoryItem', { itemId: this.selectedItem.id });
  }
  
  async equipSelectedItem() {
    if (!this.selectedItem) return;
    
    await engine.call('toggleEquipItem', { itemId: this.selectedItem.id });
  }
}

БистСма Π΄ΠΈΠ°Π»ΠΎΠ³ΠΎΠ²

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ HTML-структуры для систСмы Π΄ΠΈΠ°Π»ΠΎΠ³ΠΎΠ² с NPC:

<div class="wg-dialog-system">
  <div class="wg-dialog-portrait">
    <img id="npcPortrait" src="npc/merchant.png" alt="NPC">
    <div class="wg-dialog-name" id="npcName">Π’ΠΎΡ€Π³ΠΎΠ²Π΅Ρ†</div>
  </div>
  
  <div class="wg-dialog-content">
    <div class="wg-dialog-text" id="dialogText">
      ΠŸΡ€ΠΈΠ²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽ тСбя, ΠΏΡƒΡ‚Π½ΠΈΠΊ! Π§Ρ‚ΠΎ ΠΏΡ€ΠΈΠ²Π΅Π»ΠΎ тСбя Π² наш Π³ΠΎΡ€ΠΎΠ΄?
    </div>
    
    <div class="wg-dialog-options" id="dialogOptions">
      <!-- Π’Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² Π±ΡƒΠ΄ΡƒΡ‚ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Ρ‹ динамичСски -->
    </div>
  </div>
</div>

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π΄ΠΈΠ°Π»ΠΎΠ³Π°ΠΌΠΈ:

class DialogSystem {
  constructor() {
    this.currentDialog = null;
    this.dialogHistory = [];
    this.npcData = null;
    
    this.initialize();
  }
  
  async initialize() {
    // Подписка Π½Π° события Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    engine.subscribe('dialogStarted', (data) => {
      this.startDialog(data.npcId, data.dialogId);
    });
    
    engine.subscribe('dialogEnded', () => {
      this.endDialog();
    });
  }
  
  async startDialog(npcId, dialogId) {
    // ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… NPC ΠΈ Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    this.npcData = await engine.call('getNpcData', { npcId });
    this.currentDialog = await engine.call('getDialogData', { dialogId });
    
    // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ NPC
    document.getElementById('npcPortrait').src = this.npcData.portrait;
    document.getElementById('npcName').textContent = this.npcData.name;
    
    // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ сообщСния Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    this.displayDialogNode(this.currentDialog.startNode);
    
    // ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π΄ΠΈΠ°Π»ΠΎΠ³ΠΎΠ²ΠΎΠ΅ ΠΎΠΊΠ½ΠΎ
    document.querySelector('.wg-dialog-system').classList.add('visible');
  }
  
  displayDialogNode(nodeId) {
    const node = this.currentDialog.nodes.find(n => n.id === nodeId);
    if (!node) return;
    
    // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ тСкста Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    document.getElementById('dialogText').textContent = node.text;
    
    // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²
    const optionsContainer = document.getElementById('dialogOptions');
    optionsContainer.innerHTML = '';
    
    node.options.forEach(option => {
      // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° условий для отобраТСния Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° ΠΎΡ‚Π²Π΅Ρ‚Π°
      if (option.condition && !this.checkCondition(option.condition)) {
        return;
      }
      
      const optionElement = document.createElement('div');
      optionElement.className = 'wg-dialog-option';
      optionElement.textContent = option.text;
      
      optionElement.addEventListener('click', () => {
        this.selectOption(option);
      });
      
      optionsContainer.appendChild(optionElement);
    });
    
    // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° "Π’Ρ‹Ρ…ΠΎΠ΄" Ссли это ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹ΠΉ ΡƒΠ·Π΅Π»
    if (node.isEnd || node.options.length === 0) {
      const exitOption = document.createElement('div');
      exitOption.className = 'wg-dialog-option wg-dialog-exit';
      exitOption.textContent = 'Π—Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡŒ Ρ€Π°Π·Π³ΠΎΠ²ΠΎΡ€';
      
      exitOption.addEventListener('click', () => {
        this.endDialog();
      });
      
      optionsContainer.appendChild(exitOption);
    }
    
    // Анимация появлСния тСкста
    wgAnimate.animate(document.getElementById('dialogText'), {
      opacity: [0, 1]
    }, {
      duration: 300,
      easing: 'ease-out'
    });
    
    // Анимация появлСния Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²
    wgAnimate.animate(optionsContainer.children, {
      opacity: [0, 1],
      transform: ['translateY(10px)', 'translateY(0)']
    }, {
      duration: 300,
      delay: (el, i) => 100 + i * 50,
      easing: 'ease-out'
    });
  }
  
  checkCondition(condition) {
    // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° условия Ρ‡Π΅Ρ€Π΅Π· Π΄Π²ΠΈΠΆΠΎΠΊ
    return engine.call('checkDialogCondition', { condition });
  }
  
  async selectOption(option) {
    // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²Ρ‹Π±ΠΎΡ€Π° Π² ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    this.dialogHistory.push({
      text: option.text,
      timestamp: Date.now()
    });
    
    // Π’Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ дСйствий, связанных с Π²Ρ‹Π±ΠΎΡ€ΠΎΠΌ
    if (option.actions) {
      for (const action of option.actions) {
        await engine.call('executeDialogAction', { action });
      }
    }
    
    // ΠŸΠ΅Ρ€Π΅Ρ…ΠΎΠ΄ ΠΊ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌΡƒ ΡƒΠ·Π»Ρƒ Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    if (option.nextNode) {
      this.displayDialogNode(option.nextNode);
    } else {
      this.endDialog();
    }
  }
  
  endDialog() {
    // Π‘ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ Π΄ΠΈΠ°Π»ΠΎΠ³ΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠΊΠ½Π°
    document.querySelector('.wg-dialog-system').classList.remove('visible');
    
    // Бброс Π΄Π°Π½Π½Ρ‹Ρ… Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    this.currentDialog = null;
    
    // Π£Π²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ Π΄Π²ΠΈΠΆΠΊΠ° ΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠΈ Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    engine.call('dialogCompleted', {
      npcId: this.npcData.id,
      history: this.dialogHistory
    });
    
    // Бброс истории Π΄ΠΈΠ°Π»ΠΎΠ³Π°
    this.dialogHistory = [];
    this.npcData = null;
  }
}

// Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ систСмы Π΄ΠΈΠ°Π»ΠΎΠ³ΠΎΠ²
const dialogSystem = new DialogSystem();

Бтилизация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²

Wudgine ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ CSS ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ для Π»Π΅Π³ΠΊΠΎΠΉ настройки внСшнСго Π²ΠΈΠ΄Π° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²:

:root {
  /* ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ Ρ†Π²Π΅Ρ‚Π° */
  --wg-primary-color: #4a90e2;
  --wg-secondary-color: #5a6268;
  --wg-danger-color: #e25c5c;
  --wg-success-color: #5ce25c;
  --wg-warning-color: #e2c25c;
  --wg-info-color: #5ccde2;
  
  /* Π¦Π²Π΅Ρ‚Π° тСкста */
  --wg-text-color: #ffffff;
  --wg-text-color-secondary: #b8b8b8;
  --wg-text-color-disabled: #6c757d;
  
  /* Π€ΠΎΠ½ΠΎΠ²Ρ‹Π΅ Ρ†Π²Π΅Ρ‚Π° */
  --wg-bg-color: #1e2124;
  --wg-bg-color-light: #2c3035;
  --wg-bg-color-lighter: #3a3f44;
  
  /* Π“Ρ€Π°Π½ΠΈΡ†Ρ‹ */
  --wg-border-color: #3a3f44;
  --wg-border-radius: 4px;
  
  /* Π’Π΅Π½ΠΈ */
  --wg-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  
  /* Π Π°Π·ΠΌΠ΅Ρ€Ρ‹ ΡˆΡ€ΠΈΡ„Ρ‚ΠΎΠ² */
  --wg-font-size-small: 12px;
  --wg-font-size-normal: 14px;
  --wg-font-size-large: 16px;
  --wg-font-size-xlarge: 20px;
}

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ шаги

ПослС изучСния ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² интСрфСйсов Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅:

  1. ΠΠ΄Π°ΠΏΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ прСдставлСнныС ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ΠΏΠΎΠ΄ свои Π½ΡƒΠΆΠ΄Ρ‹
  2. ΠšΠΎΠΌΠ±ΠΈΠ½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ для создания слоТных интСрфСйсов
  3. Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ собствСнныС стили с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ CSS ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ…
  4. Π˜Π½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ интСрфСйсы с ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠΉ Π»ΠΎΠ³ΠΈΠΊΠΎΠΉ Ρ‡Π΅Ρ€Π΅Π· JavaScript API
Для Π±ΠΎΠ»Π΅Π΅ слоТных интСрфСйсов рСкомСндуСтся ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ соврСмСнныС Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈ, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ React ΠΈΠ»ΠΈ Vue.js, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ с Wudgine.

Wudgine β€’ Β© 2025