Перейти к содержанию
Pavel2000

Скриптовая система глобальных достижений для CoP

Рекомендуемые сообщения

 

Скриптовая система глобальных достижений для CoP
Автор: @Pavel2000

Интеграция в игру:
1) Создаём файл achievements_global.script в gamedata\scripts

Спойлер

--pavel2000
achievements_global = achievements_global or {}

local DEBUG = true
local FILE_NAME = "achievements_global.dat"

local data = {}
local loaded = false

-- Связка: id из achievements.ltx -> info_portion из xr_statistic.script
local achievement_info = {
    test_achievement_1 = "test_achievement_1_gained",
	test_achievement_2 = "test_achievement_2_gained",
}

local function dbg(msg)
    if not DEBUG then return end
    local text = "[achievements_global] " .. tostring(msg)
    if printf then
        printf("%s", text)
    elseif log then
        log(text)
    end
end

local function to_bool(v)
    return v == "1" or v == "true" or v == "yes"
end

local function from_bool(v)
    return v and "1" or "0"
end

local function count_data()
    local n = 0
    for _ in pairs(data) do
        n = n + 1
    end
    return n
end

function achievements_global.get_info_name(id)
    return achievement_info[id]
end

function achievements_global.has(id)
    local info = achievement_info[id]
    return data[id] == true or (info and has_alife_info(info))
end

function achievements_global.load()
    data = {}

    dbg("load(): opening file: " .. FILE_NAME)

    local f = io.open(FILE_NAME, "r")
    if not f then
        dbg("load(): file not found or cannot be opened")
        return false
    end

    local lines = 0
    for line in f:lines() do
        local key, value = line:match("^([^=]+)=(.+)$")
        if key and value then
            data[key] = to_bool(value)
            lines = lines + 1
        else
            dbg("load(): skipped bad line: " .. tostring(line))
        end
    end

    f:close()
    dbg("load(): ok, loaded lines = " .. tostring(lines))
    return true
end

function achievements_global.save()
    dbg("save(): opening file for write: " .. FILE_NAME)

    local f = io.open(FILE_NAME, "w")
    if not f then
        dbg("save(): FAILED to open file")
        return false
    end

    local written = 0
    for key, value in pairs(data) do
        f:write(key, "=", from_bool(value), "\n")
        written = written + 1
    end

    f:close()
    dbg("save(): ok, written entries = " .. tostring(written))
    return true
end

-- Синхронизация: если achievement уже есть в save, но ещё не записан в файл
function achievements_global.sync_from_game()
    local changed = false

    for id, info in pairs(achievement_info) do
        if has_alife_info(info) and not data[id] then
            data[id] = true
            changed = true
            dbg("sync_from_game(): found in save -> " .. id .. " (" .. info .. ")")
        end
    end

    if changed then
        achievements_global.save()
    end
end

-- Восстановление: если achievement есть в файле, но нет в save, выдаём info_portion тихо
function achievements_global.restore_to_game()
    for id, value in pairs(data) do
        if value then
            local info = achievement_info[id]
            if info and not has_alife_info(info) then
                dbg("restore_to_game(): giving info_portion -> " .. id .. " (" .. info .. ")")
                db.actor:give_info_portion(info)
            end
        end
    end
end

function achievements_global.init()
    if loaded then
        dbg("init(): already loaded, skip")
        return
    end

    dbg("init(): start")
    achievements_global.load()
    achievements_global.sync_from_game()
    achievements_global.restore_to_game()
    loaded = true
    dbg("init(): done, entries = " .. tostring(count_data()))
end

-- Вызывается из оригинальных functor-ов после выдачи достижения
function achievements_global.unlock(id)
    local info = achievement_info[id]

    dbg("unlock(): " .. tostring(id))

    if not data[id] then
        data[id] = true
        achievements_global.save()
    end

    -- На всякий случай синхронизируем info_portion
    if info and db and db.actor and not has_alife_info(info) then
        dbg("unlock(): giving missing info_portion -> " .. tostring(info))
        db.actor:give_info_portion(info)
    end

    return true
end

function achievements_global.lock(id)
    dbg("lock(): " .. tostring(id))

    if data[id] ~= nil then
        data[id] = false
        achievements_global.save()
    end
end

function achievements_global.reset()
    dbg("reset(): clearing all data")
    data = {}
    achievements_global.save()
end

function achievements_global.dump()
    dbg("dump(): begin")
    for k, v in pairs(data) do
        dbg("  " .. tostring(k) .. " = " .. tostring(v))
    end
    dbg("dump(): end, total = " .. tostring(count_data()))
end

function achievements_global.self_test()
    dbg("self_test(): start")

    local test_id = "debug_test_achievement"
    if achievements_global.has(test_id) then
        dbg("self_test(): achievement already exists -> persistence WORKS")
    else
        dbg("self_test(): achievement not found -> unlocking it now")
        achievements_global.unlock(test_id)
    end

    achievements_global.dump()
    dbg("self_test(): end")
end

 

2) Берём оригинальный bind_stalker.script и ищем функцию actor_binder:net_spawn(data)
после строчки benchmark.main() вставляем achievements_global.init()
Должно получиться вот так

	benchmark.main()
	achievements_global.init()
	return true
end

Всё интеграция завершена.

Принцип работы:

1)Создаём свои достижения как в оригинале:
Как создавать достижения объяснять не будут уроки в интернете есть.

Спойлер

function test_achievement_1()
    if not has_alife_info("test_achievement_1_gained") then
        if has_alife_info("test_achievement_1_give") then
            db.actor:give_info_portion("test_achievement_1_gained")
            news_manager.send_tip(db.actor, "st_ach_test_achievement_1t_end", nil, "test_achievement_1", nil, nil)
            achievements_global.unlock("test_achievement_1")
        end
    end
    return has_alife_info("test_achievement_1_gained")
end

function test_achievement_2()
    if not has_alife_info("test_achievement_2_gained") then
        if has_alife_info("test_achievement_2_give") then
            db.actor:give_info_portion("test_achievement_2_gained")
            news_manager.send_tip(db.actor, "st_ach_test_achievement_2_end", nil, "test_achievement_2", nil, nil)
            achievements_global.unlock("test_achievement_2")
        end
    end
    return has_alife_info("test_achievement_2_gained")
end

 


Мы видим что появилась дополнительная строчка achievements_global.unlock("test_achievement_1")
Данная функция после того как достижение выдалось стандартным методом автоматически создаёт файл achievements_global.dat в папке с игрой и заносит туда строчку test_achievement_1=1 
Далее после того как игрок откатился на раннее сохранение или начал игру заново у него всё равно останется это достижение.
2) Добавляем свои достижения в таблицу в скрипте achievements_global.script

local achievement_info = {
    test_achievement_1 = "test_achievement_1_gained",
	test_achievement_2 = "test_achievement_2_gained",
}

Ну а дальше выдаём игроку достижение стандартным способом и всё должно работать.
 

Коротко: что за что отвечает

Выдать достижение(данный способ выдаёт достижение без уведомления):

achievements_global.unlock("test_achievement_1")


Проверить достижение:

achievements_global.has("test_achievement_1")


Снять достижение:

achievements_global.lock("test_achievement_1")


Полностью сбросить всё:

achievements_global.reset()

Надеюсь кому нибудь пригодиться)


 1540412914_ezgif.com-optimize(1).gif.ec7250e918399802d8fe85d6d4fd7081.gif

«Если долго мучиться, что-нибудь получится»
Охотник за артефактами

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

Круто. Недавно только придумал концепцию мода на основе такой ачивки.

Спойлер

Там был бы стандартный серьёзный сюжет, но суть в том, что все хотели бы поиметь игрока, завуалированно делая предложения близости, которые вставляли между серьёзными диалогами и поручениями. И если сделаешь ошибку нажав не ту строчку, то будет гейская кат-сцена и позорная ачивка с которой репутация у всех становится ужасной.

 

  • Смех 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

Важная информация

Мы разместили cookie-файлы на ваше устройство, чтобы помочь сделать этот сайт лучше. Вы можете изменить свои настройки cookie-файлов, или продолжить без изменения настроек. Оставаясь на сайте, вы подтверждаете свое согласие на их использование. Политика конфиденциальности | Условия использования