• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!

Вопросы по скриптингу

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.367
Благодарности
7.815
Баллы
995
  • Первое сообщение
  • #1
Прежде чем задавать вопросы, ознакомьтесь с документацией..
1) Читать онлайн
2) Архив с офлайн-версией(chm) во вложении
 

Вложения

  • Vam_tutor.rar
    171,6 KB · Просмотры: 573
Последнее редактирование модератором:

НастасьСанна

Участник форума
Регистрация
6 Дек 2012
Сообщения
350
Благодарности
521
Баллы
325
Да, спасибо, поправила: функция B_SetCurrentLevel, CurrentLevel присваиваем после проверки.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
449
Благодарности
266
Баллы
230
НастасьСанна, вот смотрите. У вас при переходе на другую локацию выполнятся оба условия в теле функции "B_SetCurrentLevel" - это и условие загрузки и условие перехода. Если хотите, - можете проверить.
PS: Спасибо программистам Пираний за их "сюрпризы".*thumbs up*
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
при переходе на другую локацию выполнятся оба условия

Это возможно, только если функция инициализации вызывается два раза подряд. В Г1 столкнулся с подобным при взаимодействии с запертыми замками. Если двойной вывзов функции действительно имеет место при переходе из локации в локацию, то это можно обойти, например, с помощью введения таймера
Код:
Npc_SetRefuseTalk(hero,1);
и дополнительного условия
Код:
else if ((CurrentLevel == NewLevel) && !Npc_RefuseTalk(hero))
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
449
Благодарности
266
Баллы
230
Это возможно, только если функция инициализации вызывается два раза подряд.
Так и есть. Но вот хотелось бы уточнить, в каком именно месте Вы хотели вызвать счётчик отказа от диалога?
Т.е. функцию Npc_SetRefuseTalk(hero,1);
Кстати, тоже интересный подход, потому что из той же оперы, что и "hero.name". :)

Я решил подвести небольшой итог, собрав всё воедино, но с небольшими дополнениями. Вдруг кто-то захочет попробовать. Рабочий пример!
Story\Startup.d:
Код:
//константы тестовых zen-уровней:
const int ZEN_WORLD1 = 1;
const int ZEN_WORLD2 = 2;
const int ZEN_WORLD3 = 3;
const int ZEN_WORLD4 = 4;
const int ZEN_WORLD5 = 5;

//флаг защиты от повторного вызова функции B_SetCurrentLevel()
var int INIT_Level;


//запомнить, в какой локации сейчас находимся
//NewLevel - номер локи, которая сейчас загружается
func void B_SetCurrentLevel(var int NewLevel)
{
  
    var string str;//строка на время тестов
  
    if (CurrentLevel == 0)
    {
        //еще ничего не записывали в CurrentWorld  => новая игра
        str = ConcatStrings("Новая игра на уровне WORLD",IntToString(NewLevel));
        printScreen(str,3,3,FONT_SCREEN,900);
    }
    else if (CurrentLevel == NewLevel)
    {
        //загружен тот же мир, что и был => загрузка игры
        str = ConcatStrings("Загрузка игры на WORLD",IntToString(CurrentLevel));
        printScreen(str,3,3,FONT_SCREEN,900);
    }
    else
    {
        INIT_Level = 1;//устанавливаем "щит"
        
        //перешли из локации CurrentLevel в NewLevel
        str = ConcatStrings("Переход из WORLD",IntToString(CurrentLevel));
        str = ConcatStrings(str," в WORLD");
        str = ConcatStrings(str,IntToString(NewLevel));
        printScreen(str,3,3,FONT_SCREEN,900);
    };
  
    CurrentLevel = NewLevel;
    printScreen(ConcatStrings("CurrentLevel = ",IntToString(CurrentLevel)),3,7,FONT_SCREEN,900);
};

func void INIT_WORLD1()
{
    if (INIT_Level == 0)//если инициализации текущего zen-уровня ещё не было, то
    {
        B_SetCurrentLevel(ZEN_WORLD1);//выполняем функцию с проверками вариантов загрузок
    }
    else//иначе, если инициализация уже была, то
    {
        INIT_Level = 0;//сброс флага защиты
    };
};


func void INIT_WORLD2()
{
    if (INIT_Level == 0)//если инициализации текущего zen-уровня ещё не было, то
    {
        B_SetCurrentLevel(ZEN_WORLD2);//выполняем функцию с проверками вариантов загрузок
    }
    else//иначе, если инициализация уже была, то
    {
        INIT_Level = 0;//сброс флага защиты
    };
};
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
в каком именно месте Вы хотели вызвать счётчик отказа от диалога?

Я бы поставил в конце функции B_SetCurrentLevel
Код:
func void B_SetCurrentLevel(var int NewLevel)
{
    if (CurrentLevel == 0)
    {
        //еще ничего не записывали в CurrentWorld  => новая игра
        printScreen("Новая игра",3,3,FONT_SCREEN,900);
    }
    else if((CurrentLevel == NewLevel) && !Npc_RefuseTalk(hero))
    {
        //загружен тот же мир, что и был => загрузка игры
        printScreen("Загрузка",3,3,FONT_SCREEN,900);
    }
    else
    {
        //перешли из локации CurrentLevel в NewLevel
        printScreen("Переход",3,3,FONT_SCREEN,900);
    };

    CurrentLevel = NewLevel;
    Npc_SetRefuseTalk(hero,1);
    printScreen(ConcatStrings("CurrentLevel = ",IntToString(CurrentLevel)),3,7,FONT_SCREEN,900);
};

При первом вызове функции B_SetCurrentLevel выполняются тот блок задач, который должен выполняться, и устанавливается таймер. Если повторный вызов происходит в течение 1 секунды после первого, то блок задач, выполняемых при загрузке сохранения, не выполняется. Если в этой функции планируется сделать что-то при инициализации локации после перехода из другой локации, и эти действия нельзя выполнять дважды, то дополнительное условие можно навесить и на этот блок.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
449
Благодарности
266
Баллы
230
Я бы поставил в конце функции B_SetCurrentLevel
Я пробовал сделать так. В итоге вместо перехода пишет: "Загрузка", а вместо загрузки, наоборот, пишет: "Переход". Сейчас ещё раз попробую.
Не, не правильно работает. После загрузки игры RefuseTalk висит в "1"(в единице), поэтому и не даёт пройти условию:
Код:
else if((CurrentLevel == NewLevel) && !Npc_RefuseTalk(hero))
Поэтому срабатывает оставшееся условие "else".
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
Возможно, здесь срабатывает не очевидная на первый взгляд особенность движка, когда команды выполняются не в той последовательности, в которой расположены в скриптах. Можно попробовать внести установку таймера внутрь блоков If
Код:
func void B_SetCurrentLevel(var int NewLevel)
{
    if (CurrentLevel == 0)
    {
        //еще ничего не записывали в CurrentWorld  => новая игра
        Npc_SetRefuseTalk(hero,1);
        printScreen("Новая игра",3,3,FONT_SCREEN,900);
    }
    else if((CurrentLevel == NewLevel) && !Npc_RefuseTalk(hero))
    {
        //загружен тот же мир, что и был => загрузка игры
        Npc_SetRefuseTalk(hero,1);
        printScreen("Загрузка",3,3,FONT_SCREEN,900);
    }
    else
    {
        //перешли из локации CurrentLevel в NewLevel
        Npc_SetRefuseTalk(hero,1);
        printScreen("Переход",3,3,FONT_SCREEN,900);
    };

    CurrentLevel = NewLevel;
    printScreen(ConcatStrings("CurrentLevel = ",IntToString(CurrentLevel)),3,7,FONT_SCREEN,900);
};
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
449
Благодарности
266
Баллы
230
Возможно, здесь срабатывает не очевидная на первый взгляд особенность движка, когда команды выполняются не в той последовательности, в которой расположены в скриптах.
Ну здесь же нет ни одной AI-команды.:confused:

Можно попробовать внести установку таймера внутрь блоков If
Всё равно не правильно работает.

Я выяснил, что RefuseTalk устанавливается в "1" после "загрузки игры" не зависимо от того, что мы пишем команду на ожидание "Npc_SetRefuseTalk(hero,1);" внутри функции "B_SetCurrentLevel()".
Скорей всего отказ от диалога устанавливается для того, чтобы во время загрузки, НПС не начал разговор с ГГ.

На переходах другая ситуация. Там INIT-функция по прежнему срабатывает два раза, поэтому выполняется и условие перехода и условие загрузки. Так что скорей всего "Npc_SetRefuseTalk" - это тупиковый вариант.
Прям мистика какая-то. Э-э-х... может к какой-нибудь другой теме?
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
Поэкспериментировал в Г1. Выяснилось, что двойной вызов функции инициализации следует только при первом вхождении в локацию. Первое вхождение легко отследить с помощью дополнительных или существующих переменных.

Кроме того двойной вызов можно обойти с помощью функции Wld_GetTime. В оригинале её нет, во всяком случае в Г1 нет, но несложно прописать.
Код:
//Функции определение времени от камрада Nodrog//
func int Wld_GetMinute(var int Hour,var int from,var int till)
{
    if from >=60     {        return -1;    };
  
    if Wld_IsTime(Hour,from+1,Hour,till)
    {
        return Wld_GetMinute(Hour,from+1,till);
    }
    else
    {
        return from;          
    };
    return from;
};

func int Wld_GetHour(var int from,var int till)
{
    if from >=24
    {
        return -1;  
    };
  
    if Wld_IsTime(from+1,0,till,0)
    {
        return Wld_GetHour(from+1,till);
    }
    else
    {
        return from;
    };
    return from;
};

func int Wld_GetTime()
{
    var int h;
    h = Wld_GetHour(0,23);
    h = h*60 + Wld_GetMinute(h,0,59);
    h = h + Wld_GetDay() * 1440;
    return h;
};
//конец функций определения времени//
Эта функция определяет время в игровых минутах, прошедшее от начала игры.

Для теста прописал такую функцию
Код:
var int test;
var int test2;

func void test_func()
{
    if(test2 != Wld_GetTime())
    {
        test += 1;
        PrintScreen(IntToString(test),-1,(10 + 3*test),"FONT_OLD_10_WHITE.TGA",20);
        test2 = Wld_GetTime();
    };
};
Вызывается она из функций инициализации. Поскольку отсчёт времени в процессе загрузки локации, видимо, прекращается, из двух вызванных подряд функций выполняется только одна. Приведённая функция - это не готовый рецепт, а всего лишь иллюстрация идеи.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
449
Благодарности
266
Баллы
230
Интересно было бы посмотреть на полную реализацию вашей идеи. :)
Пока я вижу только счётчик с четырёхсекундной задержкой.
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
Ээ... для того, чтобы реализовать идею на практике нужно:
а) понимание того, зачем ОНО нужно, что именно нужно выполнить сразу после загрузки сохранения;
б) исходники скриптов, в которые ЭТО нужно впихнуть.

Поэтому, пока предлагаю только идеи ;).
 

Ur-tRall

Участник форума
Регистрация
16 Май 2009
Сообщения
4.100
Благодарности
2.123
Баллы
475
б) исходники скриптов, в которые ЭТО нужно впихнуть.
А спадение флажка с тру на фолс в алхимии подойдет? При переходе в одну локацию, навык не изучен. При возвращении в локацию навык меняет флажок и если ты выучил алхимию в другой локации. то перейдя в другую локацию можешь остаться на всю игру без выученнного навыка. Или это немного из другой оперы?
 

Leonion

Участник форума
Регистрация
31 Мар 2008
Сообщения
249
Благодарности
154
Баллы
195
Вопрос: что делать, если соурсер (или я или комп) сошел с ума?

Когда я нажимаю "save as", он мне радостно показывает папку, полную проектов. Все есть, все круто.
Если бы не одно но: ни проводник винды, ни тотал коммандер не видят в этой папке (Gothic Projects) абсолютно ничего.
И еще одно но: если я пытаюсь ctrlC-ctrlV через окно соурсера (там, где видны файлы) в другое место на компе, ничего не происходит. Как будто их не существует там, а соурсер живет в каком-то иллюзорном мире.
И еще одно но: соурсер не хочет сохранять компилированные .dat и .bin. Точнее, он вроде как их сохраняет (судя по логу), но они не обновляются в папке.
У кого-нибудь такое было?
Хеелп.)

Что мучаю: Гильдии (Возвращение), если это имеет значение.
Скрин прилагается.

P.S. Ааа, простите, случайно не в ту тему написал. Хотел в "Не стесняюсь спросить.... ". Если можно, перенесите плиз.
 

Вложения

  • Untitled.png
    Untitled.png
    251,1 KB · Просмотры: 265
Последнее редактирование:

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.367
Благодарности
7.815
Баллы
995
Вопрос: что делать, если соурсер (или я или комп) сошел с ума?
Лечить..! обоих :D

Как я нажимаю "save as", он мне радостно показывает папку, полную проектов. Все есть, все круто.
Если бы ни одно: ни проводник винды, ни тотал коммандер не видят в этой папке (Gothic Projects) абсолютно ничего.
Оно и не удивительно, учитывая кашу в твоих каталогах...

И еще одно но: если я пытаюсь ctrlC-ctrlV через окно соурсера (там, где видны файлы) в другое место на компе, ничего не происходит. Как будто их не существует там, а соурсер живет в каком-то илююзорном мире.
Возможно из-за винды, обычно там все нормально. Но лучше ничего и никуда не перетаскивать, особенно если это касается файлов с которыми работает ГСурсер..

И еще одно но: соурсер не хочет сохранять компилированные .dat и .bin. Точнее, он вроде как их сохраняет (судя по логу), но они не обновляются в папке.
Он и не должен сохранять в этих форматах. В них он только компилирует проекты, а не сохраняет. А результат компиляции по умолчанию сохраняется, например в:
Х:\GothicSourcer V3.14\Gothic Projects\SolTest2x2\PrjFIGHT

Возможные решения:
1. Удалить все к чертям под чистую, Сурсер, его Проекты и прочее...
2. Убедится что все удалено через проводник, прихлопывая каталоги которые остались(вдруг?!)
3. Установить Сурсер заново, по этой схеме:
3.1. Инсталлятор и саму прогу после установки запускать с правами админа.
3.2. Сурсер ставить желательно в другой каталог и в корень диска, а еще лучше на другой диск, например в:
Х:\Tools\GothicSourcer V3.14
или просто в корень
Х:\GothicSourcer V3.14

Должно помочь..
Точнее подскажут те, кто Вынь 7 юзает, у меня такой нет...
 

Leonion

Участник форума
Регистрация
31 Мар 2008
Сообщения
249
Благодарности
154
Баллы
195
Спасибо за совет, помогло.)

А у меня 2 новых вопроса, если я еще не сильно всех достал:

Как можно прописать функцию, которая каждый день в 0:00 (или другое время дня, главное, чтобы раз в день каждый день) осуществляла бы некое действие (если быть точным, делала бы (при var int x, var int y) следующее: x = x - y), вне зависимости от того, спит ГГ или нет?

Видел в инете странный вариант с использованием устаревшей B_RefreshArmor, но как я понял с немецкого форума, нужно, чтобы в мире кто-то в этот момент был голым (опять же вопрос, в любом месте всего мира или строго в данной локации).
Еще, как я понимаю, можно распихать это:
if (CurrentEveryDay < Wld_GetDay())
{
x=x-y;
CurrentEveryDay = Wld_GetDay();
};
по всем щелям (ГГ лег спать, ГГ впил зелье здоровья, съел кусок мяса и т.п.) и надеяться, что игроки все-таки чем-то из этого занимаются, но это как-то криво.
Еще была мысль с:
func int EveryDayStuff() {
if(Wld_IsTime(0,0,23,59))
{
if (CurrentEveryDay < Wld_GetDay())
{
x=x-y;
CurrentEveryDay = Wld_GetDay();
};
};
};
Будет ли адекватно работать такой вариант, если его просто засунуть в скрипты (ну т.е. не привязывая к какому-то событию/диалогу/etc.)?
______________
И второй момент: я нигде не нашел разъяснений по AI_PrintScreen и PrintScreen.
К примеру:
AI_PrintScreen(concattextschulden,-1,YPOS_GoldTaken,FONT_Screen,2);
PrintScreen(PRINT_LearnRunes,-1,-1,FONT_Screen,2);

Я только понял, что 1ое значение - то, что выводим на экран. А что значат остальные 4?
И есть ли разница между AI_PrintScreen и PrintScreen?
 

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.367
Благодарности
7.815
Баллы
995
В разделе Модостроения, в темах Скриптинга "Поиском" поищи Тутор Вама. Почитаешь многие вопросы отпадут. + В самой программе есть толковая справка по функциям игры, Читай документацию к проге...
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.407
Благодарности
3.232
Баллы
525
Как можно прописать функцию, которая каждый день в 0:00 (или другое время дня, главное, чтобы раз в день каждый день) осуществляла бы некое действие (если быть точным, делала бы (при var int x, var int y) следующее: x = x - y), вне зависимости от того, спит ГГ или нет?

Думаю, альтернативы циклическому триггеру здесь нет. Все остальные костыли не дадут 100% надёжности.
 

Dimus

★★★★★★★★★
Супермодератор
Регистрация
19 Июл 2010
Сообщения
5.574
Благодарности
4.168
Баллы
915
Leonion написал(а):
Вопрос: что делать, если соурсер (или я или комп) сошел с ума?
Когда я нажимаю "save as", он мне радостно показывает папку, полную проектов. Все есть, все круто.
Если бы не одно но: ни проводник винды, ни тотал коммандер не видят в этой папке (Gothic Projects) абсолютно ничего.
На самом деле комп не сошёл с ума, просто вы забыли про особенность винды начиная с висты: если GS установлен в каталог Program Files, то на самом деле проекты сохраняются в т.н. виртуальном хранилище (путь %LOCALAPPDATA%\VirtualStore\Program Files\GothicSourcer v3.14\Gothic Projects).
 

Ur-tRall

Участник форума
Регистрация
16 Май 2009
Сообщения
4.100
Благодарности
2.123
Баллы
475
А подробнее можно узнать, как переназначить путь сохранения. По моему указывать при сохранении проекта в Соусере нельзя на виндах, начиная с висты.
 

Dimus

★★★★★★★★★
Супермодератор
Регистрация
19 Июл 2010
Сообщения
5.574
Благодарности
4.168
Баллы
915
Зачем оно тебе, урра? Если не нравится длинный путь к проектам, находящимся в виртуальном хранилище, то не ставь GothicSourcer в каталог Program Files. Например, я не видел инфы насчёт возможности изменения пути к каталогу Gothic Projects.
 
Последнее редактирование:
Сверху Снизу