Одевание предметов

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Так, что-то я в погоне за идеалом и постоянными оптимизациями где-то сам себя наебал...
У меня есть табло inventory, в нём есть ячейка dressed_slot. Если там 0, значит предмет лежит в кармане. Если там номер слота — предмет вставлен в слот. Номер слота выкусывается из ячейки item_type, которая имеет по значению на каждый слот, плюс несколько абстрактных значений вроде "квестовое", "подарок", "хлам", нувыпоняли.

И всё выглядело превосходно ровно до момента одевания предметов одного типа в разные слоты (кольца, свитки, итд.).

Готового кода нету, я завис как раз в процессе, взгляд замылился и я не вижу то самое элементарное элегантное решение. Алгоритм выглядит примерно следующим образом:

PHP:
// одеть предмет
function dressitem($id)
{
    /*
     * FFFFUUUUUUU!!!!!!!!!!!!
     * 1. получаем id одеваемого предмета
     * 2. смотрим в кокой слот он хочет
     * 3. проверяем пустой ли слот
     * 4. если слот не пустой, освобождаем слот
     * 5. привязываем предмет к слоту
     *
     * если предмет кольцо
     * 1. смотрим три слота колец
     * 2. если слот не пустой смотрим слебующий слот
     * 3. если слот пустой одеваем предмет в слот
     * 4. если все три слота не пустые освобождаем последний слот
     * 5. привязываем предмет к слоту
     */
    global $user;

    //$items = new DressedItems($_SESSION['uid']);
    //$items->undressItem($_GET['drop']);

    $selectedItem = db::c()->query('SELECT * FROM `inventory` WHERE item_id = ?i AND owner_id = ?i AND `dressed_slot` = 0', $id, $_SESSION['uid'])->fetch_object();
    $itemInSlot = db::c()->query('SELECT * FROM inventory WHERE owner_id = ?i AND dressed_slot = ?i', $_SESSION['uid'], $selectedItem['item_type']);
    if ($itemInSlot->getNumRows()) {
        // снимаем предмет и одеваем поверх
    } else {
        // просто одеваем предмет в слот
    }
    if (in_array($selectedItem->item_type, [9, 10, 11])) { //кольцабля
        $wearedRings = db::c()->query('SELECT * FROM `inventory` WHERE owner_id = ?i AND dressed_slot IN (9,10,11)', $_SESSION['uid'])->fetch_object();
/*
* $wearedRings->getNumRows
* считаем количество занятых слотов колец
* если заняты три слота, снимаем третий слот, одеваем предмет в третий слот
* если заняты два слота, одеваем предмет в третий слот
* если занят один слот, одеваем предемет во второй слот
* если занято 0 слотов, одеваем предмет в первый слот
*
* если у нас 30 свитков \ расходуемых предметов — охуеваем.
*/
        for ($i = 1; $i <= 3; $i++) {
            if (!$wearedRings->dressed_slot) {
                db::c()->query('UPDATE inventory SET dressed_slot = '); //...
            }
        }
    }

    if ($selectedItem) {
        db::c()->query('UPDATE inventory SET dressed_slot = item_type WHERE id = ?i', $selectedItem['item_id']);
    }

...

Но я уже заебался пытаться эти реализовать, не бобавляя много лишних запросов и не делая дополнительных полей в базу.

Я вообще в ту сторону думаю?
 
Последнее редактирование:

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Пока что рисуется что-то такое, но выглядит как форменное ебланство...
PHP:
define('HELMET', 1);
define('ARMOR', 2);
define('LEGS', 3);
define('BOOTS', 4);
define('GLOVES', 5);
define('WEAPON', 6);
define('SHIELD', 7);
define('BELT', 8);
define('RING', 9);
define('AMULET', 10);

$selectedItemRow = db::c()->query('SELECT * FROM `inventory` WHERE item_id = ?i AND owner_id = ?i AND `dressed_slot` = 0', $id, $_SESSION['uid']);
if ($selectedItemRow->getNumRows()) {
    $selectedItem = $selectedItemRow->fetch_object();
    $itemInSlot = db::c()->query('SELECT * FROM inventory WHERE owner_id = ?i AND dressed_slot = ?i', $_SESSION['uid'], $selectedItem['item_type'])->getNumRows();
    if (in_array($selectedItem->item_type,[HELMET,ARMOR,LEGS,BOOTS,GLOVES,WEAPON,SHIELD,BELT,AMULET])) {
        //работаем с нормальными слотами
        if (!$itemInSlot) {
            // просто одеваем предмет
        } elseif ($itemInSlot === 1) {
            // снимаем предмет и одеваем вместо
        } else {
            // невозможная ситуация - два предмета в одиночном слоте. критическая ошибка, запись в лог, раздевание.
        }
    } elseif ($selectedItem->item_type === RING) {
        //работаем с кольцами
        if (!$itemInSlot) {
            // просто одеваем предмет в слот 9
        } elseif ($itemInSlot === 1) {
            // просто одеваем предмет в слот 10
        } elseif ($itemInSlot === 2) {
            // просто одеваем предмет в слот 11
        } elseif ($itemInSlot === 3) {
            // снимаем предмет из слота 11 и одеваем вместо
        } else {
            // невозможная ситуация - больше трёх предметов на три слота. критическая ошибка, запись в лог, раздевание.
        }
        // и-и-и... хуйтамплавал! потому что логика ломается нахер если, например, предметы в слотах сидят не по порядку. :(
    } else {
        //предмет вообще не должен одеваться, ошибка
    }
} else {
    //с предметом что-то сильно не ок, ошибка.
}
 
Последнее редактирование:

k880TR

Некто
📜
Сообщения
78
Реакции
7
Можно взять пример как в бк под кольца там не все слоты равны 9, там под каждый слот свой номер. 9,10,11 ты заноси сюда кольца, так же и со свитками. При одевании колец проверяем эти слоты, заняты ли они
 

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Именно от этого примера я отказался в первую очередь. Никакой гибкости. Под гибкостью я подразумеваю то, что сейчас для отображения куклы персонажа у меня используется 2 запроса в базу. Один для образа и логина, второй для всех предметов на нём. При помощи css grid я могу циклом генерировать все слоты и предметы в них. А при помощи нумерации, порядок предметов отображаются исключительно на голом css. Вот, я сделал себе шаблон и плясал от него: Edit fiddle - JSFiddle - Code Playground.
там не все слоты равны 9
9 - это не слот. Это тип предмета.
 
Последнее редактирование:

oxy

Продвинутый
🏆
📜
Сообщения
403
Реакции
85
Кольцу не нужно знать куда его одевают так ведь? Кольцо в жизни это объект с каким либо характеристиками. Что оно одето знает человек. Куда одевать тоже.
Значит в инвентаре мы не должны хранить никаких значений типа dressed или slot. Только type. Если ты подтягиваешь юзера (global $user;) то храни в json все что на нем одето, типа
Если тебе нужна привязка к слоту, а она нужна ведь ты не хочешь снимая среднее кольцо, что бы сдвинулись все, это будет банально не красиво, сделай с ключами:

PHP:
$user['dressed'][self::SLOT_RING] = [1=>1234, 2=>1235, 3=>1236]
$user['dressed'][self::SLOT_HELM] = [1=>1237]
$user['dressed'][self::SLOT_SCROLL] = [1=>1238, 2=>null, 3=>null, ... 12=>1239]

А дальше все по старому, если чел одевает шмотку ты берешь ее тип и подставив в массив одетого на юзере смотришь есть ли там что то с null. Есть - одеваем, если нет снимаешь то что там одето последним ключом
 

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Еще радикальней чем мой подход. Надо его подумать. Что-то я завязывал на слот, не помню влёт.
Хм, а если вариаций dressed не использовать, как ты предлагаешь делать выборки по свободно валяющимся вещам в мешке, например? Хватать все и сравнивать айдишники с json массивами?
 
Последнее редактирование:

oxy

Продвинутый
🏆
📜
Сообщения
403
Реакции
85
WHERE id NOT IN (ид вещей что одеты) при желании можно закешировать
 

oxy

Продвинутый
🏆
📜
Сообщения
403
Реакции
85
Такой подход позволит тебе сделать минимальную связанность классов. И если захочешь допустим использовать item еще где то, там ничего лишнего не будет. А если ты когда то начнешь писать тесты, то это будет очень хорошим бонусом. Ведь подменить независимый класс который ничего не знает о окружающем мире проще, чем тянуть пол программы в тест
 

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
у меня сейчас такие страшные классы, что умереть не встать.
 

SHEFF

Интересующийся
📜
Сообщения
26
Реакции
13
Если я не ошибаюсь, то вещи должны отличаться по классам (т.е type = 1 допустим шлем, 2 = щит, 3 = кольца и тд..)
Может как-то подумать в сторону того, чтобы при одевании смотреть какие типы одеты, если допустим щит одет, снимаем старый, одеваем новый. Вроде с кольцами должен такой подход тоже пройти.. Ну и выводить соответственно из инвентаря одетые 3 предмета с типом 3 (кольцо), но нужно обязательно предусмотреть случай когда одеты 3 кольца и игрок пытается одеть еще одно, имею ввиду чтобы была замена первого слота колец.
Не факт что дельный совет, но мало ли...
 

Bayer

Специалист
⚖️
🏆
📜
Сообщения
688
Реакции
243
Как много «одето» и ни одного «надето»
 

gosu

Некто
🏆
Сообщения
298
Реакции
0
читал ,читал тему и коменты... и решил тоже свои 5 коп добавить ...
1. на..я придумывать велосипед ... (жуйкери рулит как говорит хабиля)
2.моё видение этого :
табла инв
1.айди итема
2. айди юзверя (у кого этот предмет)
3. тип предмета (оно же слот )
4.та или нет) всмысле одели или нет шмотку)

з.ы мой пьянфй вариант ёпашит)
 

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Ничо, сейчас я схожу на две недели в отпуск, потом вернусь и минимум неделю буду закрывать гешефт отсутствия, вернусь к этому коду где-то через месяц-полтора и забуду всё напрочь. :)
снимаем старый, одеваем новый.
я медленно ползу к мысли не использовать эту технику. с одной стороны удобство и меньше кликов, с другой — никаких случайных срабатываний и замена строго тех итемов, которые нужны. правда, неизвестно насколько разбалован игрок.

Пока думал о чём-то отвлечённо-десятом, внезапно придумал простое решение. О пользе отоспаться после работы.
PHP:
elseif ($selectedItem->item_type === RING) {
    /*
     * работаем с кольцами
     * выбираем из табло слоты колец
     * получаем два массива. первый - три слота колец, второй - неизвестно занятых слотов
     * array_diff() ищем значения из первого массива, которых нет во втором.
     * получаем массив значений точно пустых слотов.
     * sort($a) сортируем и берём первое значение $a[0]. в этот слот и одевается предмет
     */
    if (!$itemInSlot) {
        // просто одеваем предмет в слот 9
    } elseif ($itemInSlot > 0 && $itemInSlot < 3) {
        // пляски с массивами
    } elseif ($itemInSlot === 3) {
        // снимаем предмет из слота 11 и одеваем вместо
    } else {
        // невозможная ситуация - больше трёх предметов на три слота. критическая ошибка, запись в лог, раздевание.
    }
} else {
    //предмет вообще не должен одеваться, ошибка
}
 
Последнее редактирование:

Insallah

¯\_(ツ)_/¯
🏆
📜
Сообщения
1,450
Реакции
443
Первый промежуточный вариант.
Второй промежуточный вариант.
PHP:
function dressitem($id)
{
    //FIXME Предметы не должны одеваться, если не соответствуют требованиям!
    define('HELMET', 1);
    define('ARMOR', 2);
    define('LEGS', 3);
    define('BOOTS', 4);
    define('GLOVES', 5);
    define('WEAPON', 6);
    define('SHIELD', 7);
    define('BELT', 8);
    define('RING', 9);
    define('AMULET', 10);
    define('ERROR_TOO_MANY_ITEMS_IN_SLOTS', 'Критическая ошибка: Переполнение слота!');
    define('ERROR_UNKNOWN_ITEM_TYPE', 'Неизвестный тип предмета!');
    define('ERROR_ITEM_NOT_FOUND', 'Предмет не найден!');
    $itemInSlot = [];
    $selectedItemRow = db::c()->query('SELECT item_type FROM `inventory` WHERE item_id = ?i AND owner_id = ?i AND `dressed_slot` = 0', $id, $_SESSION['uid']);
    if ($selectedItemRow->getNumRows()) {
        $selectedItem = $selectedItemRow->fetch_object();
        $itemInSlotRow = db::c()->query('SELECT dressed_slot FROM inventory WHERE owner_id = ?i AND dressed_slot > 0 AND item_type = ?i', $_SESSION['uid'], $selectedItem->item_type);
        $itemInSlotQuantity = $itemInSlotRow->getNumRows();
        if ($itemInSlotQuantity) {
            while ($row = $itemInSlotRow->fetch_object()) {
                $itemInSlot[] = $row->dressed_slot;
            }
        }
        if (in_array($selectedItem->item_type,[HELMET,ARMOR,LEGS,BOOTS,GLOVES,WEAPON,SHIELD,BELT,AMULET])) {
            //работаем с нормальными слотами
            if (!$itemInSlotQuantity) {
                // просто одеваем предмет
                db::c()->query('UPDATE inventory SET dressed_slot = item_type WHERE item_id = ?i', $id);
            } elseif ($itemInSlotQuantity === 1) {
                // снимаем предмет и одеваем вместо
                db::c()->query('UPDATE inventory SET dressed_slot = 0 WHERE dressed_slot = ?i', $itemInSlot[0]);
                db::c()->query('UPDATE inventory SET dressed_slot = item_type WHERE item_id = ?i', $id);
            } else {
                // невозможная ситуация - два предмета в одиночном слоте. критическая ошибка, запись в лог, раздевание.
                $error = ERROR_TOO_MANY_ITEMS_IN_SLOTS;
                db::c()->query('UPDATE inventory SET dressed_slot = 0 WHERE dressed_slot BETWEEN 1 AND 12 AND owner_id = ?i', $_SESSION['uid']);
            }
        } elseif ($selectedItem->item_type == RING) {
            // работаем с кольцами
            if ($itemInSlotQuantity < 3) {
                // Сравниваем массив колец и массив слотов для колец.
                $emptyRingSlots = array_diff([9,10,11], $itemInSlot);
                // Сортируем массив свободных слотов по возрастанию.
                sort($emptyRingSlots);
                // Одеваем предмет в первый свободный слот.
                db::c()->query('UPDATE inventory SET dressed_slot = ?i WHERE item_id = ?i',$emptyRingSlots[0], $id);
            } elseif ($itemInSlotQuantity === 3) {
                db::c()->query('UPDATE inventory SET dressed_slot = 0 WHERE dressed_slot = 11');
                db::c()->query('UPDATE inventory SET dressed_slot = 11 WHERE item_id = ?i', $id);
                // снимаем предмет из слота 11 и одеваем вместо
            } else {
                // невозможная ситуация - больше трёх предметов на три слота. критическая ошибка, запись в лог, раздевание.
                $error = ERROR_TOO_MANY_ITEMS_IN_SLOTS;
                db::c()->query('UPDATE inventory SET dressed_slot = 0 WHERE dressed_slot BETWEEN 1 AND 12 AND owner_id = ?i', $_SESSION['uid']);
            }
        } else {
            //предмет вообще не должен одеваться, ошибка
            $error = ERROR_UNKNOWN_ITEM_TYPE;
        }
    } else {
        //с предметом что-то сильно не ок, ошибка.
        $error = ERROR_ITEM_NOT_FOUND;
    }

    if (isset($error)) {
        return $error;
    } else {
        return null;
    }
}
 
Последнее редактирование:
Сверху