Вернуться   Форум команды Magic Team > Создание модов > Модостроение к Gothic

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

Модостроение к Gothic Как делаются моды к Готике...

Ответ
 
Опции темы Опции просмотра
Старый 14.06.2013, 08:00   #1
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию IKARUS: скрипт-пакет

IKARUS - Это скрипт-пакет, который был разработан немецким модостроителем Sektenspinner'ом.
Этот пакет позволяет расширить нам, другим модостроителям, возможности.
Что же такое делает он, чтобы получить новые способы улучшения качества и повышения интереса к модам?

Не секрет, что возможности стандартного набора модостроителя ограничены переданными Пираньями материалами.
Но любой скриптёр, втягиваясь и осваивая язык Даедалус, спустя некоторое время утыкается в стены, которые он пытается обойти разными извращёнными способами.
У него в определённый момент возникает вопрос к себе, но адресованный реально Пираньям. "Ну почему они не сделали функцию просто получения времени" или "почему нет функции деэкипировки отдельного типа оружия?" Что, реально сложно это было?
Нет, это было абсолютно несложно, но мы имеем то, что имеем. Может ввиду того факта, что для написания игры им хватило именно этого набора функций, что они и отдали на растерзание игрокам-модостроителям.
Ладно, это лирика. Теперь перейдём к физике.

Что же нам позволяет расширить потенциал? Здесь ответ как в известной программе: "Показать всё, что скрыто!"
Пример.
Что у нас представляет объект класса C_NPC (человек, монстр)?
Это объект, которому мы можем задать порядка тридцати полей (урон, защита, вид, флаг, имя и ещё некоторые.)
Что же представляет объект класса oCNpc (тот класс, что реально используется игрой) - это класс, имеющий более 250 полей только относящихся к нему + ещё около 40, относящихся к общему классу zCVob. И это частный пример, но уже чувствуете разницу? Пускай, 99% из 100 может нам и не нужна, ну а вдруг???

Далее, продолжаем.
Пакет позволяет читать из памяти (+ изменять в памяти) процесса игры практически любую информацию. Тут главное знать, что, где и как?

Теперь немного мат.части.
Представим себе кусок памяти наглядно:
[ячейка памяти],[ячейка памяти],[ячейка памяти],[ячейка памяти],[ячейка памяти],[ячейка памяти],[ячейка памяти] и т.д.

Каждая ячейка в понятном для нас виде имеет размер 1Байт, которая хранит в себе информацию в 16-ричной системе. Также она имеет свой собственный адрес.
В языке Даедалус есть переменные и константы трех видов (int - целое цисло, float - число с плавающей точной, string - строка).
Переменные типа int и float - занимают в памяти 4 ячейки (4 Байта).
Переменные !пользовательского! типа string (которого реально нет в языке Си++) занимают в удобном для нашем понимании виде 20 ячеек (20 Байт).
Адресом переменной в памяти по сути является адрес первой ячейки.
Получается, зная адрес ячейки памяти, тип хранимой в ней информации, мы можем в любой момент времени прочитать или изменить её.

Теперь на примере конкретного объекта, глянем, что он из себя представляет в памяти:
Код:
class zCObject	
{
  var int    _vtbl;
  var int    refCtr;
  var int    hashIndex;
  var int    hashNext;
  var string objectName;
};
Объект этого класса расположен в памяти и имеет начальный адрес. Первое поле этого класса содержит в себе информацию, и имеет свой адрес, но по сути является и адресом всего объекта.
Далее, есть следующее поле, оно смещено относительно первого поля на 4 Байта. Именно столько занимает первая ячейка типа int.
И т.д.
Получаем, что если в памяти подряд находятся два элемента класса zCObject, то они предствляют следующее
[1-ый объект имеет адрес 1234(наобум) размером 4Байта+4Байта+4Байта+4Байта+20Байт(36Байт)],[второй объект уже будет иметь адрес 1234+36 и занимать столько же]
То есть по адресу 1234 +16 мы можем прочитать поле objectName первого объекта.
По адресу 1234+36+8 мы можем прочитать поле hashIndex второго объекта.

Теперь подытожим на конкретном примере:

Имеем НПС. Он в памяти имеет адрес 1000.
Глядя в описание расширенного типа oCNpc, хочу прочитать поле idx.
Он смещён относительно начала на 288 Байт.
У нас есть два способа прочитать.
Первый - получить ссылку на НПС как на объект класса oCNpc и банально прочитать:
var int x; x = oCNpc.idx;
Второй - прочитать значение ячейки памяти, с помощью функции пакета Ikarus MEM_ReadInt(var int address)
Это одна из функций, которая читает из памяти число типа int по адресу address
var int x; x = MEM_ReadInt(1000+288);

На этом вступление окончено.

ВТОРАЯ ЧАСТЬ.

Какие вообще сосредоточены возможности в пакете ИКАРУС
  • -Чтение и изменение памяти
  • -Вызов практически любой скрытой в движке игры функции

Какой практический смысл на примере (из описания автора и от меня):
  • - включение/выключение режима MARVIN
  • - постановка игры на паузу в любой момент
  • - включить-выключить дождь/снег в любой момент
  • - узнать, какая клавиша сейчас нажата игроком.
  • - найти любой ВОБ в игре и поменять ему параметры (передвинуть, удалить, сменить внешний вид и т.д)
  • - наполнить сундук барахлом или открыть запертую где-то дверь.
  • - организовать циклы.
И т.д. и т.п., главное углубиться во всё это.

ТРЕТЬЯ ЧАСТЬ.

Рассмотрим некоторые базовые функции пакета Икарус.

1. MEM_ReadInt(var int address);
2. MEM_ReadString(var int address);


Как нетрудно догадаться из названия и внешнего вида функции, первая - читает из памяти число типа int, вторая - строку, - по адресу address;

3. MEM_WriteInt(var int x, var int address)
4. MEM_WriteString(var string y, var int address)


Опять же всё очень просто: обе записывают в память по адресу address своё значение (x - число типа int, y - строковое значение).

5. func int MEM_InstToPtr (var instance inst)
6. func instance MEM_PtrToInst (var int ptr)


Здесь чуть сложнее. Первая возвращает указатель на инстанцию (число, являющееся адресом объекта в памяти ). Вторая - делает обратное, по указателю получает ссылку на инстанцию.

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

ЧЕТВЁРТАЯ ЧАСТЬ.

Интеграция пакета Икарус в скрипты (взято из документации автора):
Распаковываем архив со скриптами к себе в проект. Приведу на своём примере:
Вот моя корневая структура проекта:
..\_Intern\
..\AI\
..\Ikarus1_2
..\Items
..\Story
..\gothic.src

Как видите от оригинала отличается наличием папки Ikarus1_2
В неё просто распакованы все файлы. И в gothic.src добавлено следующее:
Spoiler:

_Intern\Constants.d
_Intern\Classes.d
AI\AI_Intern\AI_Constants.d
//Пакет Икарус
Ikarus1_2\Ikarus_Const_G2.d
Ikarus1_2\Misc.d
Ikarus1_2\oCAiHuman.d
Ikarus1_2\oCGame.d
Ikarus1_2\oCInfoManager.d
Ikarus1_2\oCItem.d
Ikarus1_2\oCMob.d
Ikarus1_2\oCNpc.d
Ikarus1_2\oCZoneMusic.d
Ikarus1_2\zCCamera.d
Ikarus1_2\zCConsole.d
Ikarus1_2\zCMenu.d
Ikarus1_2\zCMesh.d
Ikarus1_2\zCOption.d
Ikarus1_2\zCParser.d
Ikarus1_2\zCSkyController.d
Ikarus1_2\zCTexture.d
Ikarus1_2\zCTrigger.d
Ikarus1_2\zCWaynet.d
Ikarus1_2\zCWorld.d
Ikarus1_2\zCZoneZFog.d
Ikarus1_2\Ikarus.d
Ikarus1_2\float.d
//конец пакета
AI\AI_Intern\BodyStates.d


Компиляция скриптов происходит игрой. Проверять на ошибки можно и с помощью GothicSourcer'а. Он проход сделает до конца, но остановится.


ССЫЛКИ:
Тема по скрипт-пакету на WOG.de: http://forum.worldofplayers.de/forum...paket-Ikarus-3
Пакет Ikarus (версия 1.2), компиляция только игрой: http://exodus.worldofgothic.com/team...kriptpaket.zip
Пакет Ikarus (версия 1.1.4), компиляция игрой и GS: http://exodus.worldofgothic.com/team...aket_1.1.4.zip
__________________
redleha вне форума   Ответить с цитированием
9 пользователя(ей) сказали cпасибо:
DRom (14.06.2013), Hell9999 (14.06.2013), Ilot (14.06.2013), kazus23 (14.06.2013), koschiryk (05.05.2015), master gothici (16.06.2013), PapaDog (15.02.2014), Tolgetmen (14.06.2013), НастасьСанна (15.06.2013)
Старый 14.06.2013, 15:42   #2
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

УРОК 1.

Его мы посвятим такой вещи как "Список", или массив записей иначе.
К такому термину в готике относятся:
  • - список всех вобов игры
  • - список всех НПС игры
  • - список всех итемов игры
  • - инвентарь героя, сундука
  • - список квестов в журнале
  • - и т.д. Можете додумать, что ещё можно отнести к этому.

Любой из этих списков представляет собой объект класса zCListSort.
Что у нас представляет собой этот класс:

Код:
class zCListSort {
    var int compareFunc;        //int (*Compare)(T *ele1,T *ele2);
    var int data;               //T*
    var int next;               //zCListSort<T>*
};
Пока не понятно, да? Что с этим делать.
Рассмотрим на примере сундука. Это объект класса oCMobContainer.
У этого класса есть поле containList_next - это адрес как раз списка предметов внутри сундука. То есть число, хранимое в oCMobContainer.containList_next является указателем на сортированный список предметов.

Итак мы получили адрес объекта класса zCListSort с перечнем предметов.
Как у нас выглядит этот список в памяти. Внимательно! Предположим, что у нас в сундуке: золотая монета, стрела, меч, лук. Это уже отсортированный список игрой.

Давайте сделаем первые шаги к получению информации:

Код:
func void Inventar()
{
//объявляем переменную класса oCMobContainer, 
//к которой мы приравняем наш сундук.
	
	var oCMobContainer SUNDUK;
 
//Допусти мы знаем адрес это объекта в памяти и он равен 123456;
//Чтобы начать работать с сундуком, мы должны управлять его элементами.
	
	SUNDUK = MEM_PtrToInst(123456);
	
//Всё, переменная SUNDUK у нас приравнена к этому сундуку.
//Давайте теперь управлять инвентарём сундука.
	
	var zCListSort Inventar;
	Inventar = MEM_PtrToInst(SUNDUK.containList_next);
	
//Теперь мы уже можем работать с объектом Inventar как со списком предметов.
//Мы знаем, что в инвентаре находятся предметы класса oCItem.
//В поле data хранится указатель на первую итемку (золотую монету).
	
	var oCItem first_item;
	first_item = MEM_PtrToInst(Inventar.data);
	Print(first_item.name); //Здесь на экран выведется "Золотая монета"
	
//Так а где же всё остальное?
//Смотрим на поле next.
//Оно указывает на ... след.список предметов. Запутанно вроде, ан нет. 
//Вторым списком у нас является уже следующее: стрела, меч, лук.
//То есть:
	
	var zCListSort Inventar_2;
	Inventar_2 = MEM_PtrToInst(Inventar.next);
	var oCItem second_item;
	second_item = MEM_PtrToInst(Inventar_2.data);
	Print(second_item.name); //Здесь на экран выведется "Стрела"
	
//Третий предмет получим:
	
	var zCListSort Inventar_3;	//Здесь уже остаётся меч и лук.
	Inventar_3 = MEM_PtrToInst(Inventar_2.next);
	var oCItem third_item;
	third_item = MEM_PtrToInst(Inventar_3.data);
	Print(third_item.name); //Здесь на экран выведется "Меч"
	
//Четвёртый предмет получим:
	
	var zCListSort Inventar_4;	//Здесь уже остаётся лук.
	Inventar_4 = MEM_PtrToInst(Inventar_3.next);
	var oCItem fourth_item;
	fourth_item = MEM_PtrToInst(Inventar_4.data);
	Print(fourth_item.name); //Здесь на экран выведется "Лук"
	
//На этом инвентарь заканчивается. 
//И обозначает это то, что поле Inventar_4.next = 0; 
//Значит больше нет продолжения списка.
};
В этом уроке я дал вам хоть какое-то представление о том, что представляет собой любой список в игре.

А вот наглядный пример:
__________________
redleha вне форума   Ответить с цитированием
7 пользователя(ей) сказали cпасибо:
DRom (15.06.2013), Hell9999 (15.06.2013), Ilot (17.06.2013), kazus23 (05.01.2014), koschiryk (05.05.2015), PapaDog (15.02.2014), Tolgetmen (14.06.2013)
Старый 15.06.2013, 15:03   #3
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

ВЫШЕ - Первый урок.

Урок 2. Циклы и сканирование списков с помощью цикла.
Циклы.

Рассмотрим один пример c сайта WOG.de от немецкого форумита mud-freak
Функция являет собой поиск вайпойнта в мире.
Написана она ещё была тогда, когда не вышла версия 1.2 пакета ИКАРУС.
Я её немного видоизменил под версию 1.2 , но оставив функциональную часть цикла старой - построенного на метках.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SearchWaypointByName (VAR string wp)
// -------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Код:
FUNC int SearchWaypointByName(VAR string wp)
{
//Здесь вызывается объявление глобальных инстанций. 
//В нашем случае важна одна - MEM_Waynet.   
    MEM_InitGlobalInst(); 
    
//Объявляем переменные соответствующих типов.
    VAR zCWaypoint curWp;
    VAR zCListSort curItem;
    VAR zCListSort lastItem;

//приравниваем к списку curItem - список всех вайпонтов игры.        
	curItem = MEM_PtrToInst(MEM_Waynet.wplist_next);	
   
//А здесь мы инициализируем работу с метками
    MEM_InitLabels();
    VAR int label;
//Вот тут мы ставим метку. И теперь в любом месте 
//далее по программе мы можем переместиться в эту точку кода.
    label = MEM_StackPos.position;	
 
//А здесь собственно и идёт поиск

//Читаем поле data списка. Если ничего нет, значит вайнета нет. 
//Мне кажется этот кусок лишним.
        IF (!curItem.data)  
        {
            RETURN 0; 
        };

//Приравниваем к вайпойнту curWp вайпойнт, 
//адрес которого хранится в поле data.
        curWp = MEM_PtrToInst(curItem.data);	
		
//Теперь сравниваем название вайпойнта и то имя, 
//которое мы задали для поиска.
//Если совпадает, то значит мы нашли его
        IF (Hlp_StrCmp(curWp.name, wp))    
        {
//Возвращаем адрес этого вайпонта (указатель на него) 
                RETURN MEM_InstToPtr(curWp);    
        };

//Если пришли к концу листа, значит всё... Не нашли.        
        IF (!curItem.next)	
        {
            RETURN 0;  
        };
                
//После прохода списка в случае необнаружения вайпойнта, 
//нам надо дальше искать.
//Запоминаем текущий просканированный лист как lastItem.
        lastItem = MEM_PtrToInst(MEM_InstToPtr(curWp));

//А к curItem приравниваем следующий список, 
//в котором находится следующий вайпойнт.
        curItem = MEM_PtrToInst(lastItem.next);
        
//А здесь мы перескакиваем на объявленную выше метку. 
//Тем самым получается цикл.
    MEM_StackPos.position = label;   
    
//А это тоже лишнее.
    MEM_Warn("SearchWaypointByName: Schleifenfehler");
    RETURN 0;
        
};
В следующем уроке я перепишу этот пример на свой вариант, основанный на применении комбинации while-end.
__________________
redleha вне форума   Ответить с цитированием
7 пользователя(ей) сказали cпасибо:
DRom (16.06.2013), Hell9999 (16.06.2013), Ilot (17.06.2013), kazus23 (05.01.2014), koschiryk (05.05.2015), Lev-Lion (21.06.2014), Tolgetmen (15.06.2013)
Старый 15.06.2013, 22:59   #4
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

УРОК 3. Циклы.
Использование комбинации While-End.

Здесь я вам представлю тот же самый скрипт, что был в предыдущем уроке, но уже основанный не на использовании меток, а с помощью комбинации while-end.

Давайте сразу я представлю свой вариант. В нём я отрезал лишнее, что было по сути своей ненужным излишеством.
Код:
FUNC int SearchWaypointByName(VAR string wp)
{
   MEM_InitGlobalInst();
   
   VAR zCWaypoint curWp;
   VAR zCListSort curItem;
   
   curItem = _^(MEM_Waynet.wplist_next);
   while(curItem);		
		curWp = _^(curItem.data);
		IF (Hlp_StrCmp(curWp.name, wp))
		{
			Print("ВАЙПОЙНТ НАЙДЕН!");
			RETURN MEM_InstToPtr(curWp);
		};
		IF (!curItem.next)
		{
			Print("ВАЙПОЙНТ НЕ НАЙДЕН!");
			RETURN 0;
		};
		curItem = _^(curItem.next);
	end;	
};
Обратите внимание. Я использовал такое сочетание символов как _^. Это функция, которая равна функции MEM_PtrToInst. Абсолютно одно и тоже, но сокращает писанину и повышает удобочитаемость.

Итак, что у нас происходит в скрипте. Комбинация while-end представляет собой некое подобие "скобок" для цикла. Кто занимался программированием, например на Паскале, уже видел такое. Там оно выглядит так:
while (x=1) do
begin
...[код цикла]
end;


То есть, пока x=1, всё внутри begin-end будет выполняться бесконечно. Когда x станет равным 1, цикл закончится, и начнётся выполнение кода, расположенного ниже end;.
Аналогично и здесь.
Пока выражение внутри скобок while (у нас это curItem != 0) является не 0, всё внутри while-end будет выполняться постоянно.

В моём варианте происходит следующее:
Берём первый вапойнт, приравниваем его в переменной curWp, читаем его имя, сравниваем с нужным. Если это не так, то мы переходим на следующий вайпонт в списке. И до тех пор, пока мы не найдём нужный вайпойнт, или мы не дойдём до конца вайнета, цикл поиска будет выполняться. Прервать цикл (закончить) нам позволяет команда return XXX.
__________________
redleha вне форума   Ответить с цитированием
7 пользователя(ей) сказали cпасибо:
DRom (17.06.2013), Hell9999 (17.06.2013), Ilot (17.06.2013), kazus23 (05.01.2014), koschiryk (05.05.2015), Lev-Lion (21.06.2014), Tolgetmen (16.06.2013)
Старый 17.06.2013, 08:56   #5
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

УРОК 4.
Попробуем понять работу с вызовом функций движка. А также передачей параметров в стек.

В Готике существует 4 типа вызова движком функций.
  • func void CALL__stdcall (var int adr )
  • func void CALL__thiscall (var int this, var int adr )
  • func void CALL__cdecl (var int adr )
  • func void CALL__fastcall (var int ecx, var int edx, var int adr)
А также мастерами своего дела был получен список функций Готы путём ...(молчим).
Вот ссылка на то, что вам нужно: http://yadi.sk/d/R4mM_54u6C78K

Можете не читать и не вникать пока, в чём их различие, и я не буду расписывать, чем они отличаются с технической точки зрения. Лучше попробуем разобраться на конкретном примере.
Что вам нужно, чтобы попробовать поработать с этим. Приведу пример на себе, как я это сделал.

-берём одну функцию из списка, наиболее понравившуюся и понятную по смыслу.
void __thiscall oCNpc::CompleteHeal(void) 0x00736720
Я думаю, ясно, что она должна сделать. Кто разумеет английский язык, поймёт, что она должна полностью вылечить НПСа.

Давайте разберём по порядку то, что нам необходимо:
1) тип вызова - __thiscall . Это значит, что для того, чтобы вызвать функцию мы должны использовать команду CALL__thiscall. При этом обязательным параметром будет являться указатель на объект класса oCNpc (на кого вызывается функция).
2) CompleteHeal(void) - внутри скобок не указаны передаваемые параметры (void - означает отсутствие), значит, кроме указателя на oCNpc, больше ничего не понадобится.
3) 0x00736720 - это адрес функции в памяти процесса. Представлен в 16-ричном виде (HEX-формат). Чтобы вызвать функцию по адресу, нам надо перевести это число в десятичный формат.
Я, например, использую онлайн-конвертер (http://www.binaryhexconverter.com/he...al-converter);
вы также можете использовать инженерный калькулятор Windows. Нам нужно число, идущее после 0x
00736720 (hex) = 7563040 (dec);
А теперь пишем функцию:
Код:
func void Npc_CompeteHeal(var C_NPC slf)
{
	const int oCNpc__CompleteHeal = 7563040;
	CALL__thiscall(MEM_InstToPtr(slf), oCNpc__CompleteHeal);
};
Теперь приведу пример посложнее. Выберем функцию, где нужно помимо просто указателя на НПС передать ещё какие-то параметры. Судя из названия, НПС должен перевестись в бессознательное состояние.
void __thiscall oCNpc::DropUnconscious(float, oCNpc*) 0x00735EB0
Итак:
1) Функция thiscall. Передавать обязательно надо указатель объект класса oCNpc.
2) Помимо этого нам надо передать число типа float и ещё один объект класса oCNpc. Если включить мозг, то можно предположить, что второй объект oCNpc - это походу обидчик (тот, кто переводит первого НПС в нокдаун). А вот что за число типа float, я так опытным путём и не выяснил. Видимо, Пираньи хотели заложить в него время, которое НПС будет в бессознанке, но в итоге это было реализовано в обычных скриптах в ZS_Unconscious. Но это не означает, что нам его не надо передавать. Пусть 0, пусть 101.478, неважно.
3) Переводим адрес: 00735EB0 = 7560880 ;

А вот теперь запоминаем. Параметры, которые мы будем помещать в стек, должны передаваться СПРАВА - НАЛЕВО, как это написано в скобках. То есть это выглядит так:

Код:
func void Npc_DropUnconscious(var C_NPC vict, var int xxx, var C_NPC killer)
{
	const int oCNpc__DropUnconscious = 7560880;
//Сначала мы передаем указатель на обидчика
	CALL_PtrParam(MEM_InstToPtr(killer));
//А уж затем число типа float  
	CALL_FloatParam(mkf(xxx));
//Вызываем функцию перевода НПС vict в нокдаун.   	
	CALL__thiscall(MEM_InstToPtr(vict),oCNpc__DropUnconscious);
};
Заодно мы ознакомились с новой функцией - mkf(xxx). Она переводит число типа int в формат Float, но в том виде, в котором оно реально выглядит в памяти, а именно HEX-формат. То есть число 125.14 в HEX-формате выглядит как 0x42fa47ae = 1123698606 в десятичном формате.
Передача парметров в стек производится функциями:
  • func void CALL_IntParam (var int param) /* int32 */
  • func void CALL_FloatParam (var int param) /* single / zREAL */
  • func void CALL_PtrParam (var int param) /* void* */
  • func void CALL_zStringPtrParam (var string param) /* zString* */
  • func void CALL_cStringPtrParam (var string param) /* char ** */
  • func void CALL_StructParam (var int ptr, var int words) /* struct */

В нашем случае, чтобы передать указатель на второго НПС, мы использовали CALL_PtrParam, а чтобы передать число типа float - CALL_FloatParam.

Думаю, чтобы понять этот урок, нужно самому побаловаться и потыкаться.
__________________
redleha вне форума   Ответить с цитированием
7 пользователя(ей) сказали cпасибо:
DRom (17.06.2013), Hell9999 (17.06.2013), Ilot (17.06.2013), kazus23 (05.01.2014), koschiryk (05.05.2015), Lev-Lion (21.06.2014), Tolgetmen (17.06.2013)
Старый 17.06.2013, 11:31   #6
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

УРОК 5.
{Здесь я приведу примеры самописанных фукнций, помимо тех, что я уже писал выше.}
__________________
redleha вне форума   Ответить с цитированием
3 пользователя(ей) сказали cпасибо:
kazus23 (05.01.2014), koschiryk (05.05.2015), Tolgetmen (17.06.2013)
Старый 28.04.2015, 08:29   #7
Администратор
Мастер
 
Аватар для redleha
 
Регистрация: 28.02.2008
Адрес: г.Новокуйбышевск Самарская обл.
Сообщений: 1,367
Сказал(а) спасибо: 486
Поблагодарили 407 раз(а) в 178 сообщениях
Отправить сообщение для redleha с помощью ICQ Отправить сообщение для redleha с помощью Skype™
По умолчанию Re: IKARUS: скрипт-пакет

Итак. Поделюсь очень интересным механизмом, реализованным мной на скрипт-пакете Ikarus.
Этот скрипт позволяет включить все возможные диалоги с НПС во время диалога.
Для чего нужно? Для тестирования. Если у вас вдруг не выполняется какое-то условие, а марвином его решить трудно или невозможно, просто подключите данный механизм к себе в проект.
P.S. Если кому интересна утилита для перевода кода Daedalus в форматированный вид для форума, то вот: Farbeimer

А теперь объясню вкратце как это работает:

Во-первых, сканируются все топики у НПС, с которым начинается диалог. При нажатии определенной(ых) клавиш(и) происходит подмена всех условий топиков на пустышку, которая всегда даёт TRUE. И сразу же в диалоге все строчки(топики) появляются, причём с комментарием. При покидании диалога все условия возвращаются назад.

/************************************************** *****\
---------------РАБОТА С ДИАЛОГАМИ (oCINFO)---------------
\************************************************* ******/


Код:
func int Info_GetInfo(var int npcInstance, var int herInstance, var int nr_info)	//oCInfo *
{
	//oCInfo * __thiscall oCInfoManager::GetInfo(class oCNpc * oCNpc * int)	0x00702D60
	const int oCInfoManager__GetInfo = 7351648;
	var oCNpc npc;	Npc = Hlp_GetNpc(npcInstance);
	var oCNpc her;	Her = Hlp_GetNpc(herInstance);
	CALL_IntParam(nr_info);
	CALL_PtrParam(MEM_InstToPtr(Npc));
	CALL_PtrParam(MEM_InstToPtr(Her));
	CALL__thiscall(MEM_Game.infoman,oCInfoManager__GetInfo);
	return CALL_RetValAsPtr();
};
func int Info_GetInfoCount(var int npcInstance, var int herInstance)	//oCInfo *
{
	//int __thiscall oCInfoManager::GetInfoCount(oCNpc * oCNpc *)  	0x00702940
	const int oCInfoManager__GetInfoCount = 7350592;
	var oCNpc npc;	Npc = Hlp_GetNpc(npcInstance);
	var oCNpc her;	Her = Hlp_GetNpc(herInstance);
	CALL_PtrParam(MEM_InstToPtr(Npc));
	CALL_PtrParam(MEM_InstToPtr(Her));
	CALL__thiscall(MEM_Game.infoman,oCInfoManager__GetInfoCount);
	return CALL_RetValAsInt();
};

func int oCInfo_GetConditionFunc(var int oCInfo_ptr)	//symbolindex
{
	//int __thiscall oCInfo::GetConditionFunc(void)   	0x007038F0
	const int oCInfo__GetConditionFunc = 7354608;	
	CALL__thiscall(oCInfo_ptr,oCInfo__GetConditionFunc);
	return CALL_RetValAsInt();
};
func string oCInfo_GetConditionFuncName(var int oCInfo_ptr)	//symbolname;
{
	var int symbolIndex;
	symbolIndex = oCInfo_GetConditionFunc(oCInfo_ptr);
	
	var zCPar_Symbol symb;
	symb = _^(MEM_GetSymbolByIndex(symbolIndex));
	return symb.name;
};
func int oCInfo_InfoConditions(var int oCInfo_ptr)	//	TRUE|FALSE
{
	//int __thiscall oCInfo::InfoConditions(void) 	0x00703950
	//Необходимая проверка. Без неё, в условиях для диалога other != hero, self != self
	
	if(other.id == 0)	{	other = Hlp_GetNpc(hero);	};
	self = _^(MEM_InformationMan.npc);
	const int oCInfo__InfoConditions = 7354704;	
	CALL__thiscall(oCInfo_ptr,oCInfo__InfoConditions);
	return CALL_RetValAsInt();
};


Код:
//-----------------------------------------------------------
//СКАНИРОВАНИЕ ВСЕХ ДИАЛОГОВ НА ПРИНАДЛЕЖНОСТЬ К НПС, 
//-------которые сейчас актуальны
//-----------------------------------------------------------
func int B_GetCurrentInfosByNPC(var int npcInstance)
{
	var int count;	count = Info_GetInfoCount(npcInstance,hero);
	var int i; i = 0;
	var int arr; arr   = MEM_ArrayCreate();
	var int InfoPtr;
	
	while(count);
		InfoPtr = Info_GetInfo(npcInstance,hero,i);
		MEM_ArrayInsert(arr,InfoPtr);
		i += 1;
		count -= 1;
	end;
	return arr;	//Array of oCInfo*
};
//
//СКАНИРОВАНИЕ ВСЕХ ДИАЛОГОВ НА ПРИНАДЛЕЖНОСТЬ К НПС, 
//---полностью весь список с прочитанными
//
func int B_GetAllInfosByNpc(var int npcInstance)
{
	//MEM_InfoMan = _^(MEM_Game.infoman);
	var int Infos_Ptr;	Infos_Ptr = MEM_InfoMan.infoList_next;	
	var int arr;   				arr   = MEM_ArrayCreate();
	
	var oCInfo INFO_INSTANCE;
	var zCListSort ListOfInfos;
	
	While(Infos_Ptr);
		ListOfInfos = _^(Infos_Ptr);
		INFO_INSTANCE = _^(ListOfInfos.data);
		if(INFO_INSTANCE.npc == npcInstance)
		{
			MEM_ArrayInsert(arr,MEM_InstToPtr(INFO_INSTANCE));
		};
		if(ListOfInfos.next)
		{
			Infos_Ptr = ListOfInfos.next;
			ListOfInfos = _^(Infos_Ptr);
		}
		else		{Infos_Ptr = 0;};
	end;
	
	return arr;	//Array of oCInfo*
};
//
///Сканирует диалоги и возвращает массив со значениями,
// выполняется ли условие диалога. [TRUE,FALSE,FALSE,TRUE,..]
//
func int B_GetAllInfosCondByNpc(var int npcInstance)	//по идее не нужно.
{
	var int arr_adr;	arr_adr = B_GetAllInfosByNPC(npcInstance);
	var zCArray arr;	arr = _^(arr_adr);
	var int arr_length;	arr_length = arr.numInArray;
	var int arrCond;	arrCond = MEM_ArrayCreate();	
	var int info_Ptr;
	var int i; i =0;
	var int bool;
	
	while(arr_length);
		info_Ptr = MEM_ArrayRead(arr_adr,i);
		bool = oCInfo_InfoConditions(info_Ptr);
		MEM_ArrayInsert(arrCond,bool);
		arr_length -= 1;
		i += 1;
	end;
	
	return arrCond;	//array of condition all NPC's dialogs (TRUE|FALSE)
};
//
///Сканирует диалоги и возвращает массив со значениями
// порядковых номеров названий функций dia_xx_cond в символьной таблице
//
func int B_GetAllInfosCondFuncByNpc(var int npcInstance)	//return array of symbolIndex
{
	//Получаем длину массива всех диалогов НПС npcInstance
	var int arr_adr;	arr_adr = B_GetAllInfosByNPC(npcInstance);
	var zCArray arr;	arr = _^(arr_adr);
	var int arr_length;	arr_length = arr.numInArray;
	//Делаем массив (символ_индексов функций условий диалогов) = массиву всех диалогов
	var int arrFuncCond;	arrFuncCond = MEM_ArrayCreate();	
	var int info_Ptr;
	var oCInfo info;
	var int i; i =0;
	
	while(arr_length);
		info_Ptr = MEM_ArrayRead(arr_adr,i);
		info = _^(info_ptr);
		MEM_ArrayInsert(arrFuncCond,info.conditions);
		arr_length -= 1;
		i += 1;
	end;
	
	return arrFuncCond;	//array of of symbolIndex all 'dia_xxx_cond' functions
};
//
/////Функция, возвращающая всегда TRUE - идет на подмену всех функций 'dia_xxx_cond'
//
const string INFO_allwaysTRUE_COND_NAME = "INFO_allwaysTRUE_COND";
func int INFO_allwaysTRUE_COND()	{	return TRUE;};
func int _get_INFO_TRUE_COND_symbolindex()	{	return MEM_GetSymbolIndex(INFO_allwaysTRUE_COND_NAME);	};
//
//Устанавливаем условия всех диалогов в TRUE
//
func void B_SetAllInfosCond_asTRUE(var int npcInstance)
{
	if(!ARR_oCInfosFunc_ADR)
	{
		ARR_oCInfosFunc_ADR = B_GetAllInfosCondFuncByNpc(npcInstance);
		var int arr_adr;	arr_adr = B_GetAllInfosByNpc(npcInstance);
		var zCArray arr;	arr = _^(arr_adr);
		var int arr_length;  arr_length = arr.numInArray;
		var oCInfo oCInfo_inst;
		var int i; i=0;
		var string info_description;	info_description="";
		
		while(arr_length);
			oCInfo_inst = _^(MEM_ArrayRead(arr_adr,i));
			info_description = oCInfo_inst.description;
			if(oCInfo_InfoConditions(MEM_ArrayRead(arr_adr,i)))	//Если для диалога выполняется условие
			{
				if(oCInfo_inst.told && !oCInfo_inst.permanent)
				{
					info_description = ConcatStrings(info_description,"%//уже прослушано и ушло.");
				}
				else if(oCInfo_inst.told && oCInfo_inst.permanent)	//НЕВОЗМОЖНЫЙ ВАРИАНТ!
				{
					info_description = ConcatStrings(info_description,"%//уже прослушано, но постоянный");
				};
			}
			else if(!oCInfo_InfoConditions(MEM_ArrayRead(arr_adr,i)))	//Если для диалога НЕ выполняется условие
			{
				if(oCInfo_inst.told && !oCInfo_inst.permanent)
				{
					info_description = ConcatStrings(info_description,"%//уже прослушано, не постоянный и не выполняется условие.");
				}
				else if(oCInfo_inst.told && oCInfo_inst.permanent)
				{
					info_description = ConcatStrings(info_description,"%//уже прослушано, постоянный и не выполняется условие");
				}
				else if(!oCInfo_inst.told && !oCInfo_inst.permanent)
				{
					info_description = ConcatStrings(info_description,"%//не прослушано, не постоянный и не выполняется условие");
				}
				else if(!oCInfo_inst.told && oCInfo_inst.permanent)
				{
					info_description = ConcatStrings(info_description,"%//не прослушано, постоянный и не выполняется условие");
				};
			};
			oCInfo_inst.description = info_description;
			oCInfo_inst.conditions = _get_INFO_TRUE_COND_symbolindex();
			
			arr_length -= 1;
			i += 1;
		end;
		
		DialogManager__UpdateInfos();
	}
	else
	{
		MEM_Error("Массив для восстановления условий диалогов уже существует. Необходимо затереть на него ссылку, прежде.");
	};
};
//
//Возвращаем все условия диалогов на место
//
func void B_RestoreAllInfosCond(var int npcInstance,var int arrCondFunc)
{
	if(ARR_oCInfosFunc_ADR)
	{
		var int arr_adr; var zCArray arr;
		arr_adr = B_GetAllInfosByNpc(npcInstance);
		arr = _^(arr_adr);
		var int arr_length;  arr_length = arr.numInArray;
		var oCInfo oCInfo_inst;
		var int i; i=0;
		var string info_description;	info_description="";
		
		while(arr_length);
			oCInfo_inst = _^(MEM_ArrayRead(arr_adr,i));
			info_description = oCInfo_inst.description;
			info_description = STR_Split(info_description,"%",0);
			oCInfo_inst.description = info_description;
			oCInfo_inst.conditions = MEM_ArrayRead(arrCondFunc,i);
			arr_length -= 1;
			i += 1;
		end;
		MEM_ArrayFree(ARR_oCInfosFunc_ADR);
		ARR_oCInfosFunc_ADR = 0;
		DialogManager__UpdateInfos();
	}
	else
	{
		MEM_Warn("Отсутствует массив для восстановления условий диалогов. Вероятно он был затёрт.");
	};
};
//
//--Функция вызывается в цикле (у нас в ds_time)
//При нажатии клавиш Info_DeblockKey_1 и Info_DeblockKey_2 все диалоги покажутся
//При нажатии клавиши Info_BlockKey_1 всё восстановится по умолчанию.
//!!!!Эти действия производить в рамках одного НПС и в течение одной сессии игры.!!!!
//
const int Info_DeblockKey_1 = KEY_LEFTARROW;		// -->> DarkSaga2.ini
const int Info_DeblockKey_2 = KEY_RIGHTARROW;	// -->> DarkSaga2.ini
const int Info_BlockKey_1 = KEY_TAB;							// -->> DarkSaga2.ini
const string Info_DeblockKey_Comb = "AND";

func void B_DSG_DeblockAllInfos()
{
	var int LeftArrow;		LeftArrow = MEM_KeyState(Info_DeblockKey_1);
	var int RightArrow;	RightArrow = MEM_KeyState(Info_DeblockKey_2);
	var int TabKey;				TabKey = MEM_KeyState(Info_BlockKey_1);
	//В(ы)ключаем деблокировочный ключ, который активирует все диалоги у НПС.
	if(hero.aivar[AIV_INVINCIBLE] == TRUE)
	{
		var oCNpc npc; npc = _^(MEM_InformationMan.npc);
		if((LeftArrow == KEY_PRESSED) && (RightArrow == KEY_PRESSED))
		{
			B_SetAllInfosCond_asTRUE(Hlp_GetInstanceID(npc));
		};
		if(TabKey == KEY_PRESSED)
		{
			B_RestoreAllInfosCond(Hlp_GetInstanceID(npc),ARR_oCInfosFunc_ADR);
		};
	};
};
__________________
redleha вне форума   Ответить с цитированием
3 пользователя(ей) сказали cпасибо:
Hell9999 (28.04.2015), koschiryk (05.05.2015), Tolgetmen (05.05.2015)
Старый 05.10.2016, 04:50   #8
Новичок
 
Аватар для Дикус
 
Регистрация: 12.12.2015
Сообщений: 11
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию Re: IKARUS: скрипт-пакет

Делал телекинез. Где накосячил?

Файлы spell_telekinesis и Spell_ProcessMana_Release

https://yadi.sk/d/G-ijEs6DwF39R

Вот какой косяк:

https://yadi.sk/i/e6Nln76fwF29Z

Если добавляю в Spell_ProcessMana

PHP код:
if(activespell == SPL_TELEKINESIS)
    {
        return 
spell_logic_telekinesis(manainvested);
    }; 
то игра вообще вылетает при попытке использовать заклинание.
Дикус вне форума   Ответить с цитированием
Старый 05.10.2016, 10:54   #9
Профессор скриптологии
Ученик
 
Аватар для НастасьСанна
 
Регистрация: 10.12.2012
Адрес: Самара
Сообщений: 233
Сказал(а) спасибо: 366
Поблагодарили 327 раз(а) в 135 сообщениях
Отправить сообщение для НастасьСанна с помощью ICQ
По умолчанию Re: IKARUS: скрипт-пакет

Цитата:
Сообщение от Дикус Посмотреть сообщение
Делал телекинез. Где накосячил?
У тебя есть инстанция oCItem_Telekinesis, в которой должен быть записан предмет, который надо двигать. А ты его туда нигде не записываешь.
Вот это не сработает:
Код:
		var int temp;
		temp = oCItem_Telekinesis;
		MEM_AssignInst(temp, Npc.focus_vob);
Надо либо:
Код:
		MEM_AssignInst(oCItem_Telekinesis, Npc.focus_vob);
Либо:
Код:
		oCItem_Telekinesis = MEM_PrtToInst(Npc.focus_vob);
__________________
Сколько всего не сделано. Сколько всего предстоит не сделать!
НастасьСанна вне форума   Ответить с цитированием
Старый 05.10.2016, 11:23   #10
Новичок
 
Аватар для Дикус
 
Регистрация: 12.12.2015
Сообщений: 11
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию Re: IKARUS: скрипт-пакет

А Spell_ProcessMana не трогать, как я понимаю?

Что-то совсем запутался.

1. Сначала заменил

PHP код:
                var int temp;
        
temp oCItem_Telekinesis;
        
MEM_AssignInst(tempNpc.focus_vob); 
на

PHP код:
MEM_AssignInst(oCItem_TelekinesisNpc.focus_vob); 
Получил тот же результат.

2. Заменил на

PHP код:
oCItem_Telekinesis MEM_PrtToInst(Npc.focus_vob); 
Получил ошибку: Неопределенная функция MEM_PRTTOINST

Последний раз редактировалось Дикус; 05.10.2016 в 12:07.
Дикус вне форума   Ответить с цитированием
Ответ


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 
Опции темы
Опции просмотра
Комбинированный вид Комбинированный вид

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход


Текущее время: 11:06. Часовой пояс GMT +3.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot
Magic Team© 2006-2013, The development and modification