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

  • 0
Infest

[RA-Модули] Серия скриптов для xEdit.

Вопрос

В данный момент на нексусе лежат ряд скриптов для xEdit, благодаря которым можно значительно улучшить модули и проект RA в целом. 

По порядку:

 

1. Decompress Records.

Убирает сжатие с записей, на который стоит флаг "Compressed". Вес .esm файлов увеличится, однако при том объёме, который состоит SFW RA, мы значительно можем увеличить прирост производительности и снять нагрузку на движок игры. 

 

2. Disabled LOD Crash Fixer.

Убираем глюченные объекты с флагом "Has Tree LOD", вызывающие вылеты игры. 

 

3. Simple Cell Save Bloat Fixer

Одна из основных проблем игры с большим количеством модов - это раздувание сохранений до неграбельных масштабов. Проблема в том, что все динамические объекты, после того, как ГГ или актор сдвинет их с места, прописывают свои новые координаты в файлах сохранения. Учитывая сколько в SFW-RA интерьеров и экстерьеров, файлы сохранения могут достигать >100 мб. Данный скрипт решает эту проблему тем, что ставит всем динамическим объектам специальный флаг, благодаря которому информация об объектах не сохраняется в игровом мире. Тем самым избавляя файлы сохранения от тонны записываемого туда мусора. 

  • Нравится 2
  • Спасибо! 1

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


Ссылка на сообщение

44 ответа на этот вопрос

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

  • 0
В 20.11.2021 в 17:42, MagnuMspec сказал:

Добавлю сюда еще этот момент: https://www.nexusmods.com/newvegas/articles/55233

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

Насколько мне известно в последней обнове Geck Extender даже исправили генерацию фейсгена без компресии, которая была сломанной и была необходимость использовать DDS Converter и совершать разного рода манипуляции. По этому генерация теперь еще проще. 

 

В 01.11.2021 в 23:26, MagnuMspec сказал:

п.1 -нужно тестирование.

Теперь эта функция одна из функций Гекка по умолчанию. По этому вероятно все плагины, которые проганяются через ГЕКК теряют этот вредный флаг.
Собственно можно пройтись по модулям скриптом и снять флаг.
Насколько я знаю одна возможная проблема связанная с этим флагом может возникать с фейсгеном.
Когда отключена автогенерация и убрать флаг на записи уже существующего нпс, то фейсген может потребовать перегенерации.
Но во первых в сборке фейсгена предварительно сгенерированого почти нету и стоит автоматическая генерация.
А во вторых, если автоматическая генерация будет отключена, то в любом случае тебе через Гекк нужно будет делать новый фейсген с уже снятыми флагами с нпс.
 

Снимок.PNG

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


Ссылка на сообщение
  • 0

 

Простановщик Persistent-Флагов для неписей и креачур.

Фиксит неспавнящихся существ/нпц и исчезающие трупы (которые в игре "изначально мертвы").

Изменено пользователем Trust

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


Ссылка на сообщение
  • 0

ДОБАВЛЯТОР ВЫДЕЛЕННЫХ ЛОКАЦИЙ В ЗАДАВАЕМЫЙ ЗАГРУЗОЧНЫЙ ЭКРАН.

  1. Скрипт не копирует записи которые НЕ WRLD / CELL
  2. пропускает дубли
  3. умеет работать с внешними мастерами либо с собственными (плагина) записями
  4. спрашивает бэйс-форм загрузочного экрана
  5. там проверка, что введённый айдишник, это экран загрузки
  6. добавляет в LSCR все выделенные локации
  7. и не делает ошибок
  8. СДЕЛАНО ПО ПРОСЬБЕ МАГНУМА
{
  Filename: AddLocationsToLSCR.pas
  Author:   ChatGPT (OpenAI)
  License:  MIT

  Description:
    Adds selected CELL and WRLD records to a specified LSCR (Loading Screen).
    Supports both external masters and same-file (self) locations.
    Prevents duplicates and provides detailed logging.
}


unit UserScript;

var
  LoadScreen: IInterface;
  AddedCount, SkippedCount: integer;

// Search LSCR by FormID across all loaded files
function FindLSCRByFormID(fullID: integer): IInterface;
var
  i: integer;
  rec: IInterface;
begin
  Result := nil;
  for i := 0 to FileCount - 1 do begin
    rec := RecordByFormID(FileByIndex(i), fullID, True);
    if Assigned(rec) and (Signature(rec) = 'LSCR') then begin
      Result := rec;
      Exit;
    end;
  end;
end;

// Find index of a master file inside given file's master list
function FindMasterIndex(containerFile, masterFile: IInterface): integer;
var
  i: integer;
  masterName, targetName: string;
begin
  Result := -1;
  targetName := GetFileName(masterFile);
  for i := 0 to MasterCount(containerFile) - 1 do begin
    masterName := GetFileName(MasterByIndex(containerFile, i));
    if masterName = targetName then begin
      Result := i;
      Exit;
    end;
  end;
end;

// Check if this location already exists in LSCR\Locations
function LocationExists(locations: IInterface; localID: integer): boolean;
var
  i: integer;
  lnam, direct: IInterface;
  existingID: integer;
begin
  Result := False;
  for i := 0 to ElementCount(locations) - 1 do begin
    lnam := ElementByIndex(locations, i);
    direct := ElementByPath(lnam, 'Direct');
    if Assigned(direct) then begin
      existingID := GetNativeValue(direct);
      if existingID = localID then begin
        Result := True;
        Exit;
      end;
    end;
  end;
end;

// Create a new LNAM entry and set Direct to local FormID
function CreateLocationEntry(lscr, locations, e: IInterface): IInterface;
var
  lnam, direct, baseRec: IInterface;
  baseID, localID: integer;
  masterIndex: integer;
  baseFile, lscrFile: IInterface;
begin
  Result := nil;

  // Resolve to original record
  baseRec := MasterOrSelf(e);
  baseFile := GetFile(baseRec);
  lscrFile := GetFile(lscr);

  baseID := FormID(baseRec) and $00FFFFFF;

  // SELF CASE: LSCR and location in the same file
  if GetFileName(lscrFile) = GetFileName(baseFile) then begin
    masterIndex := GetLoadOrder(lscrFile);  // MASTER SELF INDEX
    localID := (masterIndex shl 24) or baseID;
    AddMessage('SELF: Location "' + Name(e) + '"');
  end else begin
    // External master case
    masterIndex := FindMasterIndex(lscrFile, baseFile);
    if masterIndex < 0 then begin
      AddMessage('ERROR: Master file for "' + Name(e) + '" not found in LSCR file masters.');
      Exit;
    end;
    localID := (masterIndex shl 24) or baseID;
  end;

  // Prevent duplicates
  if LocationExists(locations, localID) then begin
    AddMessage('SKIP: Location "' + Name(e) + '" already exists in LSCR');
    Inc(SkippedCount);
    Exit;
  end;

  // Create new LNAM
  lnam := ElementAssign(locations, HighInteger, nil, False);
  if not Assigned(lnam) then begin
    AddMessage('ERROR: Could not create LNAM entry for "' + Name(e) + '"');
    Exit;
  end;

  direct := ElementByPath(lnam, 'Direct');
  if not Assigned(direct) then begin
    AddMessage('ERROR: LNAM has no Direct element for "' + Name(e) + '"');
    Exit;
  end;

  SetEditValue(direct, IntToHex(localID, 8));
  Inc(AddedCount);
  Result := lnam;
end;

// Add one location to LSCR
function AddLocation(lscr, e: IInterface): IInterface;
var
  locations: IInterface;
begin
  Result := nil;
  locations := ElementByPath(lscr, 'Locations');
  if not Assigned(locations) then
    locations := Add(lscr, 'Locations', True);

  if not Assigned(locations) then begin
    AddMessage('ERROR: Cannot access or create Locations container.');
    Exit;
  end;

  Result := CreateLocationEntry(lscr, locations, e);
  if Assigned(Result) then
    AddMessage('OK: Location "' + Name(e) + '" added');
end;

function Initialize: integer;
var
  inputID: string;
  id: integer;
begin
  AddedCount := 0;
  SkippedCount := 0;

  if not InputQuery('Loading Screen Editor',
                    'Enter Full FormID of LSCR (hex, e.g. 0002AE38):',
                    inputID) then
  begin
    Result := 1;
    Exit;
  end;

  try
    id := StrToInt('$' + Trim(inputID));
  except
    AddMessage('ERROR: Invalid FormID format.');
    Result := 1;
    Exit;
  end;

  LoadScreen := FindLSCRByFormID(id);
  if not Assigned(LoadScreen) then begin
    AddMessage('ERROR: LSCR with FormID "' + inputID + '" not found in any file.');
    Result := 1;
    Exit;
  end;

  AddMessage('INFO: Selected LSCR = ' + Name(LoadScreen));
end;

function Process(e: IInterface): integer;
begin
  if not Assigned(LoadScreen) then Exit;
  if (Signature(e) <> 'CELL') and (Signature(e) <> 'WRLD') then Exit;
  AddLocation(LoadScreen, e);
end;

function Finalize: integer;
begin
  AddMessage('=== Finished adding locations ===');
  AddMessage('Total added: ' + IntToStr(AddedCount));
  AddMessage('Total skipped (duplicates): ' + IntToStr(SkippedCount));
end;

end.

 

Изменено пользователем Trust
  • Нравится 2
  • Спасибо! 1

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


Ссылка на сообщение
  • 0

Скрипт от @FNVFUN, проставляющий параметры износа для брони и оружия, раскиданного по локациям. В исключения добавлено метательное оружие. Также добавил в исключения мины.

Добавляемый износ плавает между значениями в скрипте:

iMinHealth = 10;
iMaxHealth = 60;

Set Armor And Weapon Condition.pas

 

Полезно для большинства модов, так как авторы редко заморачиваются с проставлением износа.

  • Нравится 1

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


Ссылка на сообщение
  • 0

Еще один скрипт от @FNVFUN, сделанный по моей просьбе.

Замена второго и третьего знака в FormID записей на указанное значение, но только если исходный символ на этом месте = 0.

Renumber FormID SFW.pas

 

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

  • Нравится 1

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


Ссылка на сообщение
  • 0

И еще один скрипт от @FNVFUN, давно нужно было выложить.

Пересчет цен выделенных предметов на некое подобие экономики F3. В скрипте есть условия по типу предмета, зависимость от старой цены и т.д.

Это не идеальный вариант, т.к. коэффициенты подобраны усредненно, после анализа различий в ценах определенных предметов между Ф3 и Вегасом, поэтому в определенных случаях возможны перекосы, и могут потребоваться ручные правки. Но это лучше, чем пересчитывать всё руками)

ChangeValuesFromFNVToFO3.pas

  • Нравится 1

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


Ссылка на сообщение
  • 0

С чего вообще взялась эта дурь переделывать цены на манер Ф3 ? Какой в этом смысл? Уменьшить инфляцию зачеркнув пару нулей ?! ) В тройке вообще торговать особого смысла не было, ибо у торговцев крышек было с гулькин нос. В Вегасе есть же целый Разлом с бесконечными крышками. Да и сами торговцы в Мохаве далеко небедные.   

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


Ссылка на сообщение
  • 0
15 минут назад, MagnuMspec сказал:

Это не идеальный вариант, т.к. коэффициенты подобраны усредненно, после анализа различий в ценах определенных предметов между Ф3 и Вегасом, поэтому в определенных случаях возможны перекосы, и могут потребоваться ручные правки. Но это лучше, чем пересчитывать всё руками)

Хотелось бы обратный скрипт, чтобы вернул все как было в Вегасе. 

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


Ссылка на сообщение
  • 0
14 минут назад, Bulbyan сказал:

Хотелось бы обратный скрипт, чтобы вернул все как было в Вегасе. 

Тогда будет такой:

ChangeValuesFromFO3ToFNV.pas

  • Нравится 1

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


Ссылка на сообщение
  • 0
  1. Две Ренамбилки, с алгоритмами Фана, пересозданные в ЧатЖПТ, с дополнительной логикой и исключениями.
  2. Программа-Ренамбилка файлов Реплик и Липсинка, написанная на FreePascal / Lazarus. Используется алгоритм Фана.
  3. + Мои старые скрипты, конвертированные в CP-1251 кодировку.

 

  1. Trust-Change load order of FormID (FIN)-cp1251.pas
  2. Trust-Renumber FormID SFW (FIN)-cp1251.pas
  3. Trust-PERSISTENT-cp1251.pas
  4. Trust-AddLocationsToLSCR-cp1251.pas
  5. renameformidfilessimple.zip
Изменено пользователем Trust
случайно загрузил линуксовую версию...
  • Нравится 2

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


Ссылка на сообщение
  • 0
54 минуты назад, Bulbyan сказал:

С чего вообще взялась эта дурь переделывать цены на манер Ф3 ? Какой в этом смысл? Уменьшить инфляцию зачеркнув пару нулей ?!

В сборке был дикий дисбаланс между кучей предметов из Ф3 модов и добавленных вегасовскими модами. Плюс сам контент F3 в SFW имел цены Ф3, а вегасовский - вегасовские, и было странно когда одни и те же предметы по типу стоят так по-разному. Цены в Вегасе жесть какие высокие. За вход на Стрип просят показать 2к крышек, при этом для того, чтобы эту сумму получить, достаточно продать комплект боевой брони, ну или два при низком бартере. Плюс награды за квесты как правило не дотягивают до стоимости той же брони. А увеличение например сумм по квестам требовало бы правки в озвучке в отдельных вариантах.

 

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

Вариант вручную изменить цены на манер Ф3 показался самым оптимальным, плюс не изобретать велосипед, а сделать по примеру ТТВ.

 

Вот например что про это пишутТТВшники. 

Спойлер

This had to be done because FNV's economy was broken beyond repair. Actually Obsidian fixed it in a patch, but then on a later patch they broke it again because they built that patch on a version of the game before the economy balance patch was applied (or something similar).

This balancing was necessary because of how bad the economy was between the Fallout 3 side of TTW and the Fallout: New Vegas side of TTW. The super inflated prices of NV would mean that it was almost impossible to use vendor repairs on some equipment. Other issues were stuff like some weaker armors costing more than better ones, getting rich super fast just by looting stuff like a combat armor and selling it, the huge difference between FO3 equipment and FNV (including the most powerful stuff from FO3 being super cheap compared to some weak stuff from NV). And there's many other issues with the vanilla NV economy.

Why did we decided to change TTW to be more like FO3 economy instead or NV economy? For the reasons mentioned above and because as mentioned before, Obsidian did fix their Economy in a patch, but that fix got reverted (but not fully) for some reason. There are prices in later patches that are balanced around the "fixed" version, but other prices are not fixed, it's quite a mess. But it seems like the developers wanted their game economy fixed. It also prevents players from swimming in caps too quickly.

The TTW team understands that this is a "Love it" or "Hate it" change from previous versions, but it was more than necessary to make both wastelands feel like they are part of the same game and universe. Which is the main objective of TTW, that everything in it feels like it's all one giant game, instead of two games merged together.

И в этом есть логика.

 

Поискал, что есть из доступного на тему возврата цен Вегасовский вариант. 

Может подойти это: https://www.nexusmods.com/newvegas/mods/90636?tab=description

С этими конфигами: https://www.nexusmods.com/newvegas/mods/90637?tab=description

Должно работать.

 

Еще есть такой мод: Fallout 3 Economy-67919-2-0-0-1610813302.zip

Можно в нем изменить конфиг на типа обратный (увеличивающий), например на такой:

MinValue=15000 ;Minimum value items reduced can become
ValueReduction=8 ;Multiplier for item value reduction

Увеличит цены в 8 раз, но макс. цена не может стать больше 15к.

 

  • Нравится 3

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


Ссылка на сообщение
  • 0
23 часа назад, Bulbyan сказал:

С чего вообще взялась эта дурь переделывать цены на манер Ф3 ? Какой в этом смысл?

Удваиваю магнума, цены вещей из вегаса и экономика там - во многом бездумный фансервис. Экономика в оригинале Вегаса сломана намертво, но некоторые аспекты показывают, что разработчик не особо и задумывался об этом.

Банальный пример - та же Боевая броня. 
Обсидианы воткнули ей цену из Fallout 2, ну молодцы, а вот подумать о том, что в Ф2 в отличии от беседко-фалаутов броню с трупов нельзя было снимать - не, об этом как то не задумались. Вцелом именно такие приколы убили экономику вегаса, даже без абузов казино. А в реалиях СФВ стало еще хуже, т.к. в модах на Вегас на каждом углу ребята в боевой броне, с риот шотганами или марксманскими карабинами.

 

Я уж молчу про добавленный в вегасе перк Очумелые ручки, который забивает жирный х... гвоздь в крышку гроба экономики, т.к. позволяет чинить всякие вундервафли типа Крупнокалиберной винтовки, которая стоит 5600, сраными пневматическими винтовками или еще каким хламом типа охотничьих, ну или чинить силовую броню металлической. и т.д.

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

 

Я вот, например, настроил под себя экономику на базе обновленной СФВшной с ориентацией на тройку, но от себя добавил урезание лута, отключил броню с трупов(кроме сюжетной и супер уникальной), да еще и повысил цены на некоторые расходники, типа медицины и патронов. И все равно деньги копятся(особенно в вегасе, потому что награды за квесты местами избыточные), но хотя бы уже не с той безумной скоростью, а бартер перестал быть в формате пришел к барыге, выгреб у него всё бабло и пошел к следующему.

Изменено пользователем pz_4
  • Нравится 3
  • Спасибо! 1

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


Ссылка на сообщение
  • 0
10 минут назад, pz_4 сказал:

И как бы возникает обратный вопрос, а какой понт в такой экономике, где можно ворочать миллионами, которые мертвым грузом висят в карманах ГГ?

Ну будут весеть не милионы, а сотни тысяч, какая разница ?! Цены просто уменьшатся пропорционально доходу. Вы всё хотите назявать реализм не по адресу. Нью-Вегас это РПГ игра и с реализмом она изначально не дружит, а если ее с ним подружить , а дружит надо полностью,  с полной переработкой геймплея, иначе это будет такой себе реализм, местами реально, а местами нет, то в ней не останется того, за что ее любят.  

  • Нравится 2

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


Ссылка на сообщение
  • 0

 

13 минут назад, Bulbyan сказал:

Цены просто уменьшатся пропорционально доходу.

Не совсем, т.к. основные расходники для игрока, как еда, химия и патроны особо не затрагиваются такого рода переоценкой. 

16 минут назад, Bulbyan сказал:

Вы всё хотите назявать реализм не по адресу.

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

Именно ради этого и нужны подобные правки, которые убирают избыток чего бы то ни было, будь то 4д накопители, которые фактически нивелируют механику веса инвентаря игрока, избыточные цены вещей которые делают деньги фактически бесконечными, или влияние удачи в казино, который делает игру всегда в плюс(и что отключается через твик Стьюи который выставляет нейтральную удачу). Да, можно возразить, что эти правки не решают полностью проблемы, но это хотя бы шаги в сторону придания смысла геймплейным аспектам, которые по факту ломаются под весом контента вносимого модами.

 

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

  • Нравится 1

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


Ссылка на сообщение
  • 0

Я с Братишкой-Бульбианом согласен, что Фоллаут должен представлять Ъпсурт.

 

Тоесть, наркоманский перк "Очумелые Ручки" это Хорошо, нарко-длц это хорошо, етц.

  • Нравится 2

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


Ссылка на сообщение
  • 0
3 часа назад, ХАНТЕР73 сказал:

это Borderlands

 

Очнись. Это же вегусские длц, да и вообще, общий лейтмотив "мучаем моск" в длц ф3+нв. То у нас в ОВБ удалили все органы, то в ПЛ вырезали часть моска, то пришельцы мз провели эксперимент... и так без конца.

ЗЫ: Харэ флудить в скриптах. Быро кыш все из моей темы!!11 )))

Изменено пользователем Trust
  • Ха-ха 1

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


Ссылка на сообщение
  • 0

Free-Pascal / Lazarus программа ренамбера файлов реплик и файлов липсинка.

Кросс-Платформенная, компилируемая программа, ИСХОДНЫЙ КОД.

Используется Алгоритм Фана, сделал Chat-GPT.

Инструкция: Положите бинарник внутрь модовой папки Voices, где представлены папки войс-тайпов; запустите бинарник; введите 00-FF шестнадцатеричное число и нажмите Энтер.

Спойлер

 


program VoiceFormIDRenumber;

{$mode objfpc}{$H+}

uses
  SysUtils;

function IsHexDigit(c: Char): Boolean;
begin
  c := UpCase(c);
  Result := ((c >= '0') and (c <= '9')) or ((c >= 'A') and (c <= 'F'));
end;

function IsHex8(const s: string): Boolean;
var
  i: Integer;
begin
  Result := Length(s) = 8;
  if not Result then Exit;
  for i := 1 to 8 do
    if not IsHexDigit(s[i]) then
    begin
      Result := False;
      Exit;
    end;
  Result := True;
end;

function ShouldHandleExt(const ext: string): Boolean;
var
  e: string;
begin
  e := LowerCase(ext);
  Result := (e = '.lip') or (e = '.wav') or (e = '.ogg');
end;

{ Find the rightmost 8-char HEX token (between '_' or start of string) in "base".
  Returns the token ('' if not found) and the 1-based start position in startPos. }
function FindRightmostHex8(const base: string; out startPos: Integer): string;
var
  i, j, n, tokLen, sPos: Integer;
  tok: string;
begin
  Result := '';
  startPos := 0;

  n := Length(base);
  i := n;
  while i >= 1 do
  begin
    j := i;
    while (j >= 1) and (base[j] <> '_') do Dec(j);
    sPos := j + 1;                 // beginning of token
    tokLen := i - j;               // token length
    if tokLen = 8 then
    begin
      tok := Copy(base, sPos, 8);
      if IsHex8(tok) then
      begin
        Result := tok;
        startPos := sPos;
        Exit;
      end;
    end;
    i := j - 1;                    // move left to the previous token
  end;
end;

{ Build new ID from oldID with the rule:
  - 1 hex digit input: write only 3rd nibble if it is zero
  - 2 hex digits input: write 3rd nibble from upper input nibble if zero,
                        and 4th nibble from lower input nibble if zero
  The high byte (load order) and low word remain unchanged. }
function BuildNewID(oldID: LongWord; inputVal, inputLen: Integer): LongWord;
var
  highB, midB, lowW: LongWord;
  hiNib, loNib: LongWord;
  inHi, inLo: LongWord;
  changedLocal: Boolean;
begin
  highB := (oldID shr 24) and $FF;   // load order byte
  midB  := (oldID shr 16) and $FF;   // target byte (3rd and 4th nibbles)
  lowW  :=  oldID        and $FFFF;  // low 2 bytes

  hiNib := (midB shr 4) and $F;      // 3rd nibble
  loNib :=  midB        and $F;      // 4th nibble

  changedLocal := False;

  if inputLen = 1 then
  begin
    if hiNib = 0 then
    begin
      hiNib := inputVal and $F;
      changedLocal := True;
    end;
  end
  else
  begin
    inHi := (inputVal shr 4) and $F;
    inLo :=  inputVal        and $F;

    if hiNib = 0 then
    begin
      hiNib := inHi;
      changedLocal := True;
    end;
    if loNib = 0 then
    begin
      loNib := inLo;
      changedLocal := True;
    end;
  end;

  if not changedLocal then
  begin
    Result := oldID; // nothing changed
    Exit;
  end;

  midB := ((hiNib and $F) shl 4) or (loNib and $F);
  Result := (highB shl 24) or (midB shl 16) or lowW;
end;

procedure ProcessDir(const dir, rootWithDelim: string;
                     inputVal, inputLen: Integer;
                     var processed, renamed, skippedNoChange, skippedNoFormID,
                         skippedCollision, errors: Integer);
var
  SR: TSearchRec;
  searchMask: string;
  nameOnly, baseName, ext, hexOld, hexNew, renamedBase: string;
  filePath, targetName: string;
  relFile, relTarget: string;
  oldID, newID: LongWord;
  startPos: Integer;
begin
  searchMask := IncludeTrailingPathDelimiter(dir) + '*.*';

  if FindFirst(searchMask, faAnyFile, SR) = 0 then
  begin
    repeat
      // skip directories
      if (SR.Attr and faDirectory) <> 0 then
        Continue;

      nameOnly := SR.Name;
      ext := ExtractFileExt(nameOnly);
      if not ShouldHandleExt(ext) then
        Continue;

      Inc(processed);

      baseName := ChangeFileExt(nameOnly, '');

      // find rightmost 8-hex token
      hexOld := FindRightmostHex8(baseName, startPos);
      if hexOld = '' then
      begin
        Inc(skippedNoFormID);
        Continue;
      end;

      try
        oldID := LongWord(StrToInt64('$' + hexOld)); // safe for values > MaxInt
      except
        Inc(errors);
        Continue;
      end;

      newID := BuildNewID(oldID, inputVal, inputLen);
      if newID = oldID then
      begin
        Inc(skippedNoChange);
        Continue;
      end;

      hexNew := UpperCase(IntToHex(newID, 8));
      // replace in baseName at startPos with length 8
      renamedBase :=
        Copy(baseName, 1, startPos - 1) +
        hexNew +
        Copy(baseName, startPos + 8, MaxInt);

      filePath   := IncludeTrailingPathDelimiter(dir) + nameOnly;
      targetName := IncludeTrailingPathDelimiter(dir) + renamedBase + ext;

      relFile   := ExtractRelativePath(rootWithDelim, filePath);
      relTarget := ExtractRelativePath(rootWithDelim, targetName);

      if FileExists(targetName) then
      begin
        Writeln('SKIP (collision): ', relFile, ' -> ', relTarget);
        Inc(skippedCollision);
        Continue;
      end;

      if not RenameFile(filePath, targetName) then
      begin
        Writeln('ERROR: failed to rename "', relFile, '"');
        Inc(errors);
        Continue;
      end;

      Writeln(relFile, '  ->  ', relTarget);
      Inc(renamed);

    until FindNext(SR) <> 0;
    FindClose(SR);
  end;
end;

procedure RenumberFirstLevelSubdirsOnly;
var
  codeStr, s: string;
  inputVal, inputLen: Integer;
  processed, renamed, skippedNoChange, skippedNoFormID, skippedCollision, errors: Integer;
  rootDir, rootDelim, scanDir, relScan: string;
  SR: TSearchRec;
begin
  processed := 0; renamed := 0; skippedNoChange := 0;
  skippedNoFormID := 0; skippedCollision := 0; errors := 0;

  // ---- read parameter (1-2 hex) ----
  Write('Enter HEX (1-2 digits) for mid byte nibbles: ');
  ReadLn(codeStr);
  s := Trim(UpperCase(codeStr));

  // optional 0X prefix
  if (Length(s) >= 2) and (Copy(s, 1, 2) = '0X') then
    Delete(s, 1, 2);

  if (Length(s) < 1) or (Length(s) > 2) then
  begin
    Writeln('ERROR: input must be 1 or 2 hex digits');
    Writeln;
    Write('Press Enter to exit...');
    ReadLn;
    Halt(1);
  end;

  // parse HEX (with pause on error)
  try
    inputVal := StrToInt('$' + s);
  except
    Writeln('ERROR: invalid HEX: ', s);
    Writeln;
    Write('Press Enter to exit...');
    ReadLn;
    Halt(1);
  end;

  inputLen := Length(s);
  if inputLen = 1 then inputVal := inputVal and $F else inputVal := inputVal and $FF;

  rootDir  := GetCurrentDir;
  rootDelim := IncludeTrailingPathDelimiter(rootDir);

  Writeln('Root: .');
  Writeln('NOTE: files in root are skipped; only first-level subdirectories are processed.');
  Writeln('Rule: write 3rd nibble (and 4th if provided), only where zero');
  Writeln;

  // process files in first-level subdirectories only
  if FindFirst(rootDelim + '*', faAnyFile, SR) = 0 then
  begin
    repeat
      if (SR.Attr and faDirectory) <> 0 then
      begin
        // skip "." and ".."
        if (SR.Name <> '.') and (SR.Name <> '..') then
        begin
          scanDir := rootDelim + SR.Name;
          relScan := ExtractRelativePath(rootDelim, scanDir);
          Writeln('Scanning subdir: ', relScan);
          ProcessDir(scanDir, rootDelim, inputVal, inputLen,
                     processed, renamed, skippedNoChange, skippedNoFormID, skippedCollision, errors);
        end;
      end;
    until FindNext(SR) <> 0;
    FindClose(SR);
  end;

  // ---- summary ----
  Writeln;
  Writeln(Format('SUMMARY: processed=%d, renamed=%d, skipped(no-change=%d, no-formid=%d, collision=%d), errors=%d',
    [processed, renamed, skippedNoChange, skippedNoFormID, skippedCollision, errors]));

  // pause before exit
  Writeln;
  Write('Press Enter to exit...');
  ReadLn;
end;

begin
  try
    RenumberFirstLevelSubdirsOnly;
  except
    on E: Exception do
    begin
      Writeln('FATAL: ', E.ClassName, ': ', E.Message);
      Writeln;
      Write('Press Enter to exit...');
      ReadLn;
      Halt(1);
    end;
  end;
end.

 

 

 

 

renameformidfilessimple.zip

  • Нравится 1

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


Ссылка на сообщение
  • 0

Прописыватель состояния Оружия и Брони / Костюмов, для предметов в других модах.

Необходимо для соблюдения баланса стоимости добытых предметов. (10%..60%)

Скрипт игнорирует Метательное и Мины.

Скрипт обрабатывает NPC_ и CREA.

Скрипт обрабатывает REFR предметов внутри CELL / WRLD.

Скрипт обрабатывает CONT.

Скрипт пропускает оружие Can't Drop, Non-Playable, Quest Item, и так-же и для костюмов.

Автор половины всей логики -- Фан.

Автор дополнительных обвесов -- Chat-GPT.

СВЕТО-РЕЖИССЁР -- ТРАСТ.

 

Trust-SetArmorAndWeaponCondition_noLVLI_cleaned.pas

  • Нравится 1
  • Спасибо! 1

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


Ссылка на сообщение

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

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

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

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

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

Войти

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

Войти


×
×
  • Создать...