Оглавление | Сообщение об ошибках | Ваше мнение о проекте | E-Mail автору |
Все, кто развивал промышленность (до выхода версии 1.4) сталкивались с необходимостью сбрасывать излишки денег со счетов станций на свой счет (и наоборот - подпитывать станции на этапе раскрутки и активной закупки сырья).
Когда станций больше десяти (а со временем счет может дойти до сотни) это уже напрягает. Существует масса готовых скриптов, но я здесь подробно расскажу о своем скрипте - просто чтобы на этом примере могли научиться новички.
Скрипт называется station manager (как-бы нанятый управляющий). Был написан изначально для версии 1.2, но с выходом 1.4 слегка переработан (команды перенесены в консоль станций и кое-что еще по-мелочи).
Техзадание
1. Станция имеет нижнее и верхнее ограничение остатка на счете. Если денег на счете больше - излишек переводится на основной счет игрока. Если денег меньше - счет станции пополняется со счета игрока до нижнего предела. У каждой станции эти пределы выставляются индивидуально.
2. Основной счет игрока имеет нижнее ограничение. Деньги из этого резерва не могут быть использованы для пополнения счета станции (чтобы не пропадали нанятые TL).
3. Игрок сам выбирает - какие станции будут иметь управляющего. В любой момент любая станция может выключена в список управляемых, может быть исключена из этого списка.
4. В любой момент должна быть доступна статистика - список управляемых станций, количество денег, переведенных со станции (или на станцию). Нужно для выявления неэффективных (или недостаточно эффективных) станций.
Выбор способа
Очевидно, задача решается двумя основными способами.
Первый - на каждой станции свой скрипт, управляющий данной конкретной станцией. Так сделан стандартный скрипт в версии 1.4.
Поскольку до выхода этой версии к станции мог быть привязан только один скрипт (а не десять как сейчас), я выбрал тогда (для 1.2) другой способ - один глобальный скрипт, управляющий сразу всеми станциями).
Оба способа по большому счету равноправны.
Общий алгоритм действий
Основной скрипт, разбитый для удобства на три части
1. Инициализация (init.StationManager.controls)
В момент старта скрипта проверяется наличие ряда глобальных переменных. Если таковых нет - скрипт создает и регистрирует эти переменные. Помимо этого, скрипт выставляет в командное меню абсолютно всех кораблей игрока несколько команд (нанять менеджера для станции, уволить менеджера со станции, дать отчет по движению денег, установить неснижаемый остаток основного счета).
2. Основной цикл (StM.mainloop)
a. Для каждой станции в списке управляемых станций исполнить скрипт StM.justifymoney с параметрами Station (объект:станция), min (число:нижний предел остатка на счете станции), max (число:верхний предел остатка на счете станции), cfund (число:неснижаемый остаток на основном счете игрока).
b. Число, возвращенное скриптом, запомнить как сумму денег, переведенную на счет игрока (это число может быть и отрицательным).
c. Подождать определенный промежуток времени.
d. Повторить.
3. Управление счетом станции (StM.justifymoney)
Аргументы:
Station - объект:станция;
min - число:нижний предел остатка на счете станции;
max - число:верхний предел остатка на счете станции;
cfund - число:неснижаемый остаток на основном счете игрока.
Результат:
fee - число:сумма денег, переведенная на счет игрока.
a. Получить в переменную player сумму денег на счете игрока (с учетом неснижаемого остатка).
b. Получить в переменную account сумму денег на счете станции.
с. Вычислить и поместить в переменную need сумму денег, которую необходимо перевести на счет игрока (число может быть отрицательным).
d. Выислить (player + need), (account - need).
e. Установить счет игрока равным player, счет станции равным account.
f. Вернуть в вызывающий скрипт значение need.
Для чего основной алгоритм разбит на части и разбросан по разным скриптам ? Инициализация и основной цикл разделены по соображениям удобства написания и редактирования. Алгоритм регулирования счета отдельной станции вынесен в собственный скрипт из соображений удобства модификации и повторного использования кода. Встроенный язык скриптов не имеет механизма описания функций - аналогом функции является сам скрипт. В него можно передавать параметры, он может возвращать результат.
1. Найм менеджера на станцию (StM.install)
Аргументы:
Station - объект:станция;
min - число:нижний предел остатка на счете станции;
max - число:верхний предел остатка на счете станции.
a. Проверить наличие станции Station в списке управляемых станций.
b. Если станция уже в списке - установить новые значения для верхнего и нижнего пределов ее счета равными соответственно параметрам max и min, завершить скрипт.
c. Если станции в списке нет - найти в списке станцию, расположенную в том же секторе, что и Station.
d. Если такая станция найдена - включить Station в список прямо перед найденной станцией.
e. Если такая станция не найдена - добавить Station в конец cписка станций.
f. Обнулить счетчики переведенных со станции денег, установить пределы счета станции, увеличить счетчик станций в списке.
g. Поместить в logbook сообщение о найме менеджера.
Для чего такие сложности с поиском станций из того же сектора? Это нужно для того, чтобы станции из одного сектора шли в списке подряд. Тогда мы и в итоговом отчете очень легко сгруппируем станции по секторам. А группировать станции по секторам нужно для того, чтобы их названия помещались в одну строку на экране, тогда список станций в отчете будет смотреться как стройная, легко читаемая таблица. Да и легче в таком списке найти нужную строку - сначала находим сектор, затем - станцию в секторе.
2. Найм менеджеров сразу на все станции игрока (StM.install.all)
Аргументы:
min - число: нижний предел остатка на счете каждой станции;
max - число: верхний предел остатка на счете каждой станции.
a. Для каждого сектора галактики получить список станций игрока, размещенных в этом секторе.
b. Для каждой станции в списке исполнить скрипт StM.install.
3. Увольнение менеждера со станции (StM.remove)
Аргументы:
Station - объект: станция.
a. Если станция присутствует в списке станций, управляемых скрптом - исключить ее из списка и записать соответствующее сообщение в logbook.
4. Отчет о движении денег (StM.report.all)
a. Показать название сектора для первой станции в списке.
b. Для каждой станции из списка сформировать строку, содержащую:
- название станции;
- общую сумму денег, переведенную со счета станции на счет игрока за все то время, что счетом управляет нанятый менеджер;
- сумму денег, переведенную со счета станции на счет игрока за время, прошедшее с момента выдачи предыдущего отчета.
c. Если следующая станция в списке размещена в другом секторе, показать название этого сектора.
d. Сформировать заголовок отчета, содержащий текущую дату, дату выдачи предыдущего отчета, общую сумму денег, переведенных со всех станций за все время, общую сумму денег, переведенных со всех станций с момента выдачи предыдущего отчета.
e. Записать заголовок в logbook игрока, записать полный отчет во внешний log-файл.
5. Установка значния
неснижаемого остатка (StM.setcfund)
Аргумент:
cfund - число:неснижаемый остаток на основном счете игрока.
a. Установить значение cfund.
Структуры данных
Для каждой станции нам нужно хранить несколько различных параметров.
Во-первых, нужна ссылка на сам объект-станцию. Иначе мы не сможем управлять ее счетом.
Во-вторых, нужно хранить нижний и верхний пределы счета станции, общую сумму денег, перечисленных со счета станции и сумму денег, перечисленную со времени выдачи последнего отчета.
В более продвинутых языках программирования мы смогли бы создать структуру для каждой станции. В этом языке понятие структуры отсутствует, поэтому мы сделаем по одному массиву для каждого параметра.
Массив ссылок на объекты-станции назовем StMst.
Массивы верхних и нижних пределов счета назовем StMjmMax и StMjmMin соответственно.
Массив общих сумм назовем StMtotal.
Последний массив (суммы, переведенные с момента выдачи последнего отчета) назовем StMdiff.
Общее количество станций в списке будем хранить в переменной StMnum.
Сумму неснижаемого остатка счета игрока будем хранить в переменной StMcfund.
Дату выдачи последнего отчета будем хранить в переменной StMlastrep.
Поскольку ко всем этим данным доступ должны иметь несколько скриптов, все переменные должны быть глобальными. Что это значит?
Приватные, локальные и глобальные переменные.
Привязка скриптов.
При запуске любого скрипта, первое что определяет скриптовый движок - к чему, к какому объекту будет привязан скрипт. Это может быть корабль, станция, астероид - что угодно. Или же скрипт может быть не привязанным ни к одному объекту, такие скрипты являются глобальными.
У каждого скрипта могут быть приватные переменные. Собственно, какие-либо операции (математические, логические, операции сравнения) можно производить только над приватными переменными.
Каждый скрипт имеет доступ только к своим собственным приватным переменным, переменные другого скрипта ему недоступны.
Локальные переменные по сути своей напоминают файлы на жестком диске компьютера: вы можете создать файл, стереть его или перезаписать его содержимое целиком - но для редактирования файла вы должны сначала прочитать его в память компьютера, отредактировать и снова записать на жесткий диск. Точно так же дела обстоят с локальными переменными: чтобы что-то сделать с ними, вы должны сначала прочитать значение локальной переменной и присвоить его приватной переменной. Делается это командой get local variable.
Чтобы прочитать значение локальной переменной вам нужно знать ее имя (точно так же, как в случае с файлом на жестком диске) и, помимо этого, нужно знать, какому объекту эта переменная принадлежит. Чтобы присвоить локальной переменной новое значение, нужно воспользоваться командой set local variable.
Глобальные переменные от локальных отличаются тем, что (как и глобальные скрипты) они не привязаны ни к одному объекту, поэтому доступны всем скриптам просто по имени. Функции доступа к глобальым переменным назыаются get global variable и set global variable.
Теперь понятно, почему мы вынуждены для глобальных переменных выбирать длинные и сложные имена - чтобы уменьшить вероятность совпадения имен глобальных переменных у разных скриптов. С этой целью все глобальные переменные нашего скрипта начинаются с букв "StM" (Station Manager).
Но если от посторонних скриптописателей мы отделались с помощью сложных имен переменных, то с нашими собственными скриптами все обстоит немного сложнее.
Если внимательно посмотреть на алгоритмы наших скриптов, становится заметна такая вещь: легко может возникнуть ситуация, когда основной скрипт будет обрабатывать список станций и переводить деньги, а в этот момент пилот решит добавить в список управляемых новую станцию. Или захочет удалить из списка
существующую.
Что тут может быть неприятного? По логике работы с глобальными переменными, наш основной скрипт сначала прочтет глобальные переменные и присвоит прочитанные значения своим приватным переменным. Затем произведет необходимые операции со своими приватными переменными и в конце-концов запишет значения своих
приватных переменных в глобальные переменные. Само-собой, старые значения глобальных переменных при этом пропадут бесследно. Так что если во время активной работы основного скрипта StM.mainloop мы вызовем скрипт StM.install и с его помощью добавим станцию в список, то в момент завершения активной работы StM.mainloop перепишет значения глобальных переменных и внесенные ранее изменения пропадут - станция добавлена в список не будет.
Тут опять уместна аналогия с файлами в сетевом доступе. Вы открыли файл, редактируете его. В этот момент на другой машине кто-то тоже открыл тот же самый файл и тоже его редактирует. Тот из вас, кто запишет файл первым, потеряет свои изменения - после того, как второй редактирующий запишет свои изменения поверх.
Как этого избежать? Точно так же, как этого избегают в случае доступа к файлам - с помощью блокировок.
Блокировки
Смысл блокировок - дать понять всем, что кто-то сейчас редактирует значения
глобальных переменных и еще не записал изменения. Тогда те, кто тоже желает
внести измения в значения этих же переменных должны дождаться снятия блокировки.
Мы сделаем это просто. Заведем себе специальную глобальную переменную по имени StMlock. Если эта переменная имеет значение false - это будет означать, что никто в этот момент не редактирует значения глобальных переменных.
Тогда логика работы наших скриптов будет такой:
a. Получить значение StMlock
b. Если StMlock == FALSE, перейти к пункту d.
с. Перейти к пункту a.
d. Переменной StMlock присвоить значение TRUE.
После окончания редактирования значений глобальных переменных присвоить переменной StMlock значение false.
Это очень простая, примитивная блокировка. Но наш скрипт тоже не из сложных, для него такой блокировки будет достаточно.
Поскольку подобная блокировка может понадобиться для других скриптов - есть смысл оформить еще (с некоторыми дополнениями) в специальный служебный скрипт.
Скрипт блокировки ANS.lock
Аргументы
LockName - строка:имя глобальной переменной, используемой для блокировки. В нашем случае - StMlock
Pause - число:длительность паузы между проверками переменной (в миллисекундах). Чтобы дать возможность выполняться другим скриптам, мы обязательно должны делать паузу в подобных циклах проверки. Этот аргумент задает длительность паузы в миллисекундах.
Tries - число:количество проверок. Если мы не можем неопределенно долго ждать снятия блока, у нас есть возможность ограничить число попыток. Если же это число оставить равным нулю - скрипт будет ожидать снятия блокировки до победного.
Результат
Скрипт возвращает FALSE в том случае, когда после заданного числа попыток не удалось дождаться снятия блока.
Значение TRUE в результате исполнения скрипта означает, что вы теперь смело можете редактировать свои глобальные переменные.
Текст скрипта:
// Эти две строчки будут исполнены один раз при старте скрипта
001 $lock = get global variable: name=$LockName
002 $try = $Tries
//
Эти
строки
(003-011) будут
исполняться
снова
и
снова
- пока
не
исчерпаются
попытки
или
пока
глобальная
переменная
не
примет
значение
FALSE
003 while
$lock == [TRUE]
004 @ = wait $Pause ms
005 $lock = get global variable: name=$LockName
006 if $Tries != 0
007 dec $try =
008 skip if $try != 0
009 return [FALSE]
010 end
011 end
// Как только блокирующая переменная приняла значение FALSE (это означает,
что другой скрипт записал измененные значения в наши глобальные переменные), мы
тут же блокируем их, устнавливая значение блокируюшей переменной в TRUE.
012 set
global variable: name=$LockName value=[TRUE]
013 return [TRUE]
Применение
Скрипт ANS.lock вызывается в самой первой строчке каждого скрипта, меняющего значения глобальных переменных:
001 @ = [THIS] -> call script 'ANS.lock' : LockName='StMlock' Pause=2000 Tries=0
После записи новых значений в глобальные переменные обязательно нужно не забыть сбросить флаг блокировки:
057 set global variable: name='StMlock' value=[FALSE]
Дата и время. (Еще один служебный скрипт).
Поскольку в отчетах мы намерены показывать текущую дату и дату предыдущего отчета - нам понадобится скрипт, превращающий понятное компьютеру игровое время в понятную человеку игровую дату.
Дело в том, что игровое время представляет собой число секунд, прошедших с момента старта сюжета игры. Чтобы превратить его в игровую дату, применяем служебный скрипт ANS.format.date.
Аргумент:
ticks - число: число секунд с момента начала игры.
Текст скрипта:
001 $x = $ticks
// Операция целочисленного деления - секунды превращаются в целые минуты, остаток отбрасывается. Примеры: 59 / 60 = 0, 61 / 60 = 1.
002 $x = $x / 60
// Операция получения остатка от деления - получаем минуты текущего часа
003 $m = $x mod 60
// Наводим красоту - если минут меньше десяти - приписываем к числу минут ведущий нолик. Так 17:2 превращается в 17:02.
004 skip if $m > 9
005 $m = '0' + $m
// Превращаем минуты в часы и продолжаем...
006 $x = $x / 60
007 $h = $x mod 24
008 skip if $h > 9
009 $h = '0' + $h
010 $x = $x / 24 + 1
011 $d = $x mod 30
012 skip if $d > 9
013 $d = '0' + $d
014 $x = $x / 30 + 7
015 $n = $x mod 12
016 skip if $n > 9
017 $n = '0' + $n
018 $x = $x / 12 + 764
// Собираем результат в единое целое - год-месяц-день час:мин
019 $res = sprintf: fmt='%s-%s-%s %s:%s', $x, $n, $d, $h, $m
020 return $res
Применение:
044 $date = playing time
047 @ $date = [THIS] -> call script 'ANS.format.date' : ticks=$date
Автоматический запуск скриптов и назначение собственных команд
При старте игры и при загрузке сэйва автоматически запускаются скрипты с именами, начинающимися на "init." и "!plugin.".
Скрипты типа "!plugin." должны иметь цифровую подпись. Чтобы не морочить себе голову с этим, назовем свой скрипт инициализации "init.StationManager.controls".
Задача скрипта инициализации - включить в командную консоль всех баз игрока свои команды управления счетом. Кроме того, нужно будет запустить глобальный скрипт управления счетами - в том случае, если он не был запущен ранее.
Текст скрипта:
// Первый оператор включает в командную консоль всех станций игрока команду с идентификатором COMMAND_TYPE_STATION_10 и назначает этой команде скрипт для исполнения с именем StM.install.this.
001 global script map: set: key=COMMAND_TYPE_STATION_10, class=Station, race=Player, script='StM.install.this', prio=0
// Второй оператор определяет, какие апгрейды должна иметь станция, чтобы команда COMMAND_TYPE_STATION_10 была доступна в консоли станции. Значение TRUE делает команду доступной всегда.
002 set script command upgrade: command=COMMAND_TYPE_STATION_10 upgrade=[TRUE]
// Эти операторы включают остальные четыре команды
004 global script map: set: key=COMMAND_TYPE_STATION_11, class=Station, race=Player, script='StM.install.all', prio=0
005 set script command upgrade: command=COMMAND_TYPE_STATION_11 upgrade=[TRUE]
006
007 global script map: set: key=COMMAND_TYPE_STATION_12, class=Station, race=Player, script='StM.report.all', prio=0
008 set script command upgrade: command=COMMAND_TYPE_STATION_12 upgrade=[TRUE]
009
010 global script map: set: key=COMMAND_TYPE_STATION_13, class=Station, race=Player, script='StM.setcfund', prio=0
011 set script command upgrade: command=COMMAND_TYPE_STATION_13 upgrade=[TRUE]
012
013 global script map: set: key=COMMAND_TYPE_STATION_19, class=Station, race=Player, script='StM.remove.this', prio=0
014 set script command upgrade: command=COMMAND_TYPE_STATION_19 upgrade=[TRUE]
// Если глобальная переменная StMnum не определена (имеет значение null), делаем вывод о том, что скрипт инициализации запущен в первый раз (самое начало игры либо игрок только что установил наш скрипт себе на машину). В этом случае нужно инициализировать все глобальные переменные и запустить скрипт с основным циклом управления счетами.
016 $StMnum = get global variable: name='StMnum'
017 if $StMnum == null
018 set global variable: name='StMnum' value=0
019 set global variable: name='StMcfund' value=10000
// Этот оператор создает массив, не содержащий элементов - заготовку массива. Добавлять элементы в такой "пустой" массив можно оператором insert <элемент> into array <массив> at index <индекс массива>
021 $StMsts = array alloc: size=0
022 set global variable: name='StMsts' value=$StMsts
023 $StMjmMax = array alloc: size=0
024 set global variable: name='StMjmMax' value=$StMjmMax
025 $StMjmMin = array alloc: size=0
026 set global variable: name='StMjmMin' value=$StMjmMin
027 $StMtotal = array alloc: size=0
028 set global variable: name='StMtotal' value=$StMtotal
029 $StMdiff = array alloc: size=0
030 set global variable: name='StMdiff' value=$StMdiff
031
032 $StMlastrep = playing time
033 set global variable: name='StMlastrep' value=$StMlastrep
// Не забываем установить начальное значение для блокирующей переменной. В принципе, если мы не установим его - ничего страшного не произойдет. Переменная будет поначалу иметь значение null, а блокирующим является значение TRUE. Но лучше завести себе привычку в явном виде инициализировать все переменные. Такая привычка впоследствии сэкономит очень много времени и нервов.
035 set global variable: name='StMlock' value=[FALSE]
// Такая команда запускает отдельный скрипт примерно так же, как вы запускаете ракету - выстрелил-и-забыл. Дальше этот скрипт живет сам по себе, а запущенный командой START - совершенно отдельно от него.
037 @ START [THIS] -> call script 'StM.mainloop' :
038 end
040 return null
После исполнения этого скрипта (а он запускается автоматически при старте новой игры или при загрузке сэйва) мы получим в меню каждой своей станции пять команд вида ReadText2010-1110[ReadText2011-1110]. Работать команды будут так, как им положено - но хотелось бы видеть осмысленный текст в их названии.
Как команду назовете, так она и поплывет
Числа в загадочных словах ReadText2010-1110 имеют определенное значение.
Первое число (2010) - идентификатор текстовой страницы, второе - идентификатор строки.
Что это за страницы и строки?
Все тексты, присутствующие в игре (все экраны интерфейса, сообщения бортового компьютера, миссии BBS, сюжетные тексты) размещены в файпах с именами вида XXnnnn.xml (в распакованной версии). Здесь XX - это идентификатор языка текстов (44 для английского языка, 49 для немецкого, 70 для русского и т.д.). А nnnn - это номер файла. Первый файл имеет номер 0001, второй 0002 и так далее.
Внутри файлы имеют такую структуру:
<?xml version="1.0" encoding="UTF-8" ?>
<language id="идентификатор языка текстов">
<page id="номер страницы" title="название страницы" descr="описание страницы">
<t id="номер строки">строка</t>
</page>
</language>
Таким образом, каждая строка однозначно идентифицируется двумя числами - номером страницы и номером строки в пределах страницы.
Когда игра при старте или загрузке читает файлы текстов, она начинает с первого файла (440001.xml для английской версии), затем читает 440002.xml и так далее. При этом строки из последнего файла дополняют страницы, считанные из файлов с меньшими номерами.
Теперь ясно, что для корректного отображения наших команд нам нужен файл с именем, например, 440003.xml и с таким содержимым:
<?xml version="1.0" encoding="UTF-8" ?>
<language id="44">
<page id="2010" title="Commands" descr=" ">
<t id="1110">Hire manager to this station</t>
<t id="1111">Hire manager to all stations</t>
<t id="1112">Industry profit and loss statement</t>
<t id="1113">Set contigency fund</t>
<t id="1119">Fire manager from this station</t>
</page>
<page id="2011" title="Commands" descr=" ">
<t id="1110">StM</t>
<t id="1111">StM</t>
<t id="1112">StM</t>
<t id="1113">StM</t>
<t id="1119">StM</t>
</page>
</language>
Страница 2010 содержит полные названия команд. Страница 2011 содержит краткие названия тех же самых команд. Сделано это для корабельных скриптов - краткие названия отображаются в кокпите вашего корабля (где нет особо много места под длинные надписи).
В нашем случае команды не будут постоянно исполняться на станциях - этим занимается отдельный скрипт StM.mainloop, а значит краткие названия нам совсем ни к чему. Но и оставить пустые строчки мы не можем - разработчики X2 не особо напрягали себя обработкой различных "нештатных" ситуаций, так что игра просто молча умрет при запуске.
Еще два служебных скрипта. Константа THIS.
Если вы обратили внимание - в командах найма и увольнения менеджера запускаются скрипты StM.install.this и StM.remove.this. Единственное, что делают эти скрипты - запускают StM.install и StM.remove таким образом, чтобы их параметр Station содержал ссылку на станцию, в консоли которой была выбрана соответствующая команда. Вот их текст.
StM.install.this
Аргументы
min - число:нижний предел остатка на счете станции;
max - число:верхний предел остатка на счете станции.
Скрипт :
001 @ = [THIS] -> call script 'StM.install' : Select station=[THIS] Low boundary of station account=$min Top boundary of station account=$max
002 return null
Здесь мы видим подстановку константы THIS вместо параметра Station. Что это за константа?
Системная константа THIS всегда указывает на объект, из командной консоли которого был запущен скрипт. Если запускаем из командной консоли станции, THIS указывает на эту станцию. Если из консоли корабля, в котором находимся - указывает на этот корабль (тогда константы THIS и PLAYERSHIP указывают на один и тот ж объект).
Эту константу точно так же использует скрипт StM.remove.this:
001 @ = [THIS] -> call script 'StM.remove' : Select station to fire manager=[THIS]
002 return null
Сразу разберемся с еще одним простым скриптом - StM.setcfund.
StM.setcfund
Все, что он делает - устанавливает новое значение для неснижаемого остатка на счете игрока. Здесь даже не обязательно использовать блокировку - достаточно просто дать глобальной переменной новое значение.
Аргументы
cfund - число:неснижаемый остаток на основном счете игрока.
Скрипт
001 set global variable: name='StMcfund' value=$cfund
002 return null
Основной цикл управления счетами станций (StM.mainloop)
Скрипт:
// Скрипт представляет собой бесконечный цикл. Пока игрок жив - менеджеры станций должны делать свою работу. Даже если станций пока (или уже) нет.
001 while [TRUE]
// Нет смысла непрерывно сканировать счета - надо дать поработать остальным скриптам. Делаем между сканированием паузу длительностью в одну минуту.
002 @ = wait 60000 ms
// Начало активной фазы - блокируем глобальные переменные
004 @ $lock = [THIS] -> call script 'ANS.lock' : LockName='StMlock' Pause=2000 Tries=0
// Глобальные переменные заблокированы, теперь их никто не поменяет пока мы не закончим активную фазу. Считываем значения глобальных переменных.
006 $StMsts = get global variable: name='StMsts'
007 $StMnum = get global variable: name='StMnum'
008 $StMjmMin = get global variable: name='StMjmMin'
009 $StMjmMax = get global variable: name='StMjmMax'
010 $StMtotal = get global variable: name='StMtotal'
011 $StMdiff = get global variable: name='StMdiff'
012 $StMcfund = get global variable: name='StMcfund'
// Проходим по списку станций.
014 $i = 0
015 while $i < $StMnum
// Получаем очередную станцию из списка. Если станция к этому моменту уже не существует в галактике (враги сожгли родную хату) - удаляем эту станцию из общего списка и уменьшаем общее количество станций.
017 $st = $StMsts[$i]
018 if not $st -> exists
019
020 remove element from array $StMsts at index $i
021 remove element from array $StMdiff at index $i
022 remove element from array $StMjmMax at index $i
023 remove element from array $StMjmMin at index $i
024 remove element from array $StMtotal at index $i
025 dec $StMnum =
026
027 else
// Станция жива - вызываем для нее скрипт StM.justifymoney с параметрами, специфичными именно для этой станции.
029 $min = $StMjmMin[$i]
030 $max = $StMjmMax[$i]
031 $total = $StMtotal[$i]
032 $diff = $StMdiff[$i]
// Скрипт возвращает сумму денег, переведенную со счета игрока на счет станции.
034 @ $fee = [THIS] -> call script 'StM.justifymoney' : Station=$st Min=$min Max=$max cfund=$StMcfund
// Учитываем эту сумму в общей сумме платежей и в сумме платежей со времени последнего отчета.
036 $total = $total - $fee
037 $diff = $diff - $fee
038 $StMtotal[$i] = $total
039 $StMdiff[$i] = $diff
040 end
// Берем следующую станцию в списке.
042 inc $i =
043 end
// Активная фаза завершена - записываем новые значения глобальных переменных.
045 set global variable: name='StMtotal' value=$StMtotal
046 set global variable: name='StMdiff' value=$StMdiff
047 set global variable: name='StMjmMax' value=$StMjmMax
048 set global variable: name='StMjmMin' value=$StMjmMin
049 set global variable: name='StMsts' value=$StMsts
050 set global variable: name='StMnum' value=$StMnum
// Снимаем блокировку с глобальных переменных...
052 set global variable: name='StMlock' value=[FALSE]
// ... и переходим на начало бесконечного цикла - к паузе длительностью 60 сек.
054 end
055 return null
Служебный скрипт StM.justifymoney
Аргументы:
Station - объект:станция;
min - число:нижний предел остатка на счете станции;
max - число:верхний предел остатка на счете станции;
cfund - число:неснижаемый остаток на основном счете игрока.
Результат:
fee - число:сумма денег, переведенная со счета игрока на счет станции.
Текст скрипта:
// Получаем количество денег на счету станции
001 $account = $station -> get money
// Получаем количество денег на счету игрока. Сразу уменьшаем это количество на сумму неснижаемого остатка - чтобы не испытывать соблазна.
002 $player = get player money
003 $player = $player - $cfund
// По умлочанию, если количество денег на счету станции укладывается в заданные границы - переводить деньги не требуется
004 $need = 0
// Если сумма денег на счету станции ниже минимума
005 if $account < $min
// Если при этом остаток свободных денег на счету игрока меньше, чем необходимо для пополнения счета станции - переводим на счет станции все, что остается у игрока (за вычетом неснижаемгого остатка - для этого его и водили).
006 if $player < $min - $account
007 $need = - $player
// Если же денег у игрока куры не клюют - переводим на станцию столько, сколько нужно чтобы счет сравнялся с нижним пределом.
008 else
009 $need = $account - $min
010 end
011 end
// Если на счете станции больше денег, чем положено верзним пределом - сбрасываем излишек на счет игрока. На станции остается денег ровно "под пробку".
012 skip if not $account > $max
013 $need = $account - $max
// Собственно, переводы денег: сколько переводим игроку - столько же снимаем со станции.
014 add money to player: $need
015 $need = - $need
016 $station -> add money: $need
// Возвращаем сумму - сколько денег переведено на счет станции. Соответственно, столько же сняли со счета игрока.
017 return $need
Найм менеджера на станцию (StM.install)
Аргументы:
Station - объект:станция;
min - число:нижний предел остатка на счете станции;
max - число:верхний предел остатка на счете станции.
Скрипт
// Блокируем глобальные переменные чтобы спокойно распоряжаться ими
001 @ = [THIS] -> call script 'ANS.lock' : LockName='StMlock' Pause=2000 Tries=0
// Защита от дурака. Если пилот указал верхний и нижний пределы в обратном порядке - меняем их местами.
003 if $min > $max
004 $i = $min
005 $min = $max
006 $max = $i
007 end
// Если пилот в качестве пределов счета указал отрицательные числа - устанавливаем пределы равными нулю.
009 skip if $max > 0
010 $max = 0
011 skip if $min > 0
012 $min = 0
// Получаем значения глобальных переменных
014 $StMnum = get global variable: name='StMnum'
015 $StMsts = get global variable: name='StMsts'
016 $StMtotal = get global variable: name='StMtotal'
017 $StMjmMin = get global variable: name='StMjmMin'
018 $StMjmMax = get global variable: name='StMjmMax'
019 $StMdiff = get global variable: name='StMdiff'
// В этом цикле мы за один проход по списку станций находим сразу две вещи. Во-первых, проверяем наличие станции в списке. Если станция в списке есть, ее индекс будет находиться в переменной i. Если станции в списке нет, переменная i будет иметь значение StMnum. Во-вторых, находим индекс первой из станций, расположенных в том же секторе что и новая, включаемая в список станция. Если такая станция обнаружена, ее индекс будет находиться в переменной j. Тогда при добавлении cтанции в общий список, станция добавится перед всеми другими станциями своего сектора. Если не нашлось станции из этого же сектора (если мы еще не нанимали менеджера ни на одну из станций в этом секторе), переменная j будет иметь значение 0. Тогда новая станция будет добавлена в начало списка станций.
021 $i = 0
022 $j = 0
023 $sector = $Station -> get sector
024 while $i < $StMnum
025 $st = $StMsts[$i]
026 $sec = $st -> get sector
027 skip if $sec != $sector
028 $j = $i
029 skip if $st != $Station
030 break
031 inc $i =
032 end
// Если переменная i имеет значение StMnum, то есть был проверен весь список и станция в нем не обнаружена - добавляем станцию в позицию списка, на которую указывает переменная j (она указывает либо на начало списка, либо на первую станцию из того же сектора, что и новая станция)
034 if $i == $StMnum
// Оператор insert <элемент> into array <массив> at index <индекс> сдвигает на одну позицию все элементы массива начиная с того, на который указывает индекс. На освободившееся место записывется новый элемент.
036 insert $Station into array $StMsts at index $j
037 insert 0 into array $StMtotal at index $j
038 insert 0 into array $StMdiff at index $j
039 insert $min into array $StMjmMin at index $j
040 insert $max into array $StMjmMax at index $j
// Поскольку в список была добавлена одна станция - увеличиваем общее количество станций на единицу.
041 inc $StMnum =
// Присваиваем глобальным переменным новые значения
043 set global variable: name='StMsts' value=$StMsts
044 set global variable: name='StMtotal' value=$StMtotal
045 set global variable: name='StMdiff' value=$StMdiff
046 set global variable: name='StMnum' value=$StMnum
// Записываем в лог игрока подтверждающее сообщение
048 write to player logbook: printf: pageid=2010 textid=70005, $Station, $min, $max, null, null
// Если после прохода по списку станций переменная i имеет значение, отличающееся от StMnum, это означает что наша станция уже есть в списке и добавлять ее не нужно - нужно только установить для нее новые пределы счета.
049 else
050 $StMjmMin[$i] = $min
051 $StMjmMax[$i] = $max
052 write to player logbook: printf: pageid=2010 textid=70006, $Station, $min, $max, null, null
053 end
// Здесь мы сохраняем глобальные переменные StMjmMin и StMjmMax. Это нужно делать вне зависимости от того, найдена была станция в списке - или нет. Вот почему эти две команды вынесены за пределы условного оператора if.
055 set global variable: name='StMjmMin' value=$StMjmMin
056 set global variable: name='StMjmMax' value=$StMjmMax
// Как обычно, не забываем снять блокировку с глобальных переменных. Мы свою работу сделали, сохранили их - теперьнужно дать возможность сделать свою работу другим скриптам.
058 set global variable: name='StMlock' value=[FALSE]
059 return null
С операторами записи в logbook игорока давайте разберемся подробнее.
Logbook
Игрок имеет свою ленту сообщений. Каждая запись в ленте имеет дату и пояснение - что это за текст (сообщение от кого-либо или же просто запись).
Из скрипта сообщение игроку можно отправить с помощью оператора send incoming message <строка> to player: display it=<флаг> из раздела (почему-то) Audio Commands. Если флаг установлен в TRUE, сообщение немедленно будет показано игроку прямо поверх кокпита. В любом случае, текст сообщения будет записан в logbook игрока.
Простую запись можно поместить в logbook с помощью одного из трех операторов раздела Logbook Commands:
1. write to player logbook <строка>
2. write to player logbook: printf: fmt=<строка формата>, <п1>, <п2>, <п3>, <п4>, <п5>
3. write to player logbook: printf: pageid=<число>, textid=<число>, <п1>, <п2>, <п3>, <п4>, <п5>
Первый оператор записывает фиксированную строку в logbook - безо всяких изменений.
Второй оператор позволяет записать в logbook строку, в определенные места которой можно подставить различные значения - числа, названия, имена и т.д. Собственно, строка формата - это то, что мы хотим записать в logbook. А параметры п1, п2, ..., п5 - это значения, которые мы хотим в строку подставить. Этих значений может не быть вообще (тогда строка просто будет записана без изменений) или же могут быть одно, два - до пяти включительно.
Чтобы в строке формата определить место для вставки параметра, в нужном месте строки следует вставить символ '%', за которым следуют один или несколько спецсимволов, поясняющих скриптовому движку какие дополнительные действия нужно совершить с параметром, прежде чем вставить его в строку формата.
Параметры в строку формата вставляются по очереди, в том порядке, в каком в строке формата встречаются символы '%' (если я явном виде не задан иной порядок).
На сегодня известны такие комбинации спецсимволов:
%s - просто в этом месте вставить в строку параметр, как он есть.
%d - вставить параметр как число. Лично мне пока не удалось обнаружить отличий от %s, поэтому я везде использую %s вместо %d.
%!ns - вставить в этом месте параметр номер n (а не тот, который должен тут стоять в порядке очереди).
%ns - в этом месте строка формата раздвигается ровно на n симолов, в освободившееся место записывается столько символов параметра, сколько поместится. Если параметр короче чем n символов, перед символами параметра в строку включается столько пробелов, сколько недостает до n. Таким образом параметр выравнивается в по правому краю отведенного ему места.
%-ns - действует так же, как и %ns, с той лишь разницей что параметр выравнивается по левому краю отведенного ему места.
Третий оператор записи сообщений очень похож на второй. Отличие в том, что строка формата не задана в явном виде в тексте скрипта. Она будет прочитана из обсуждавшихся ранее файлов вида XXnnnn.xml (440001.xml, 440002.xml, 440003.xml и т.д. - для английской версии игры). Параметр pageid задает номер страницы, параметр textid задает номер строки в этих файлах.
Каждый оператор создает отдельное сообщение. Предположим, наше сообщение имеет сложную структуру (или оно очень длинное) и нам неудобно выводить его одним оператором. Как быть в таком случае?
В таком случае нужно "набрать" большое сообщение из нескольких мелких. Делается это оператором сложения: msg = msg1 + msg2 + msg3 и т.д. Затем одним оператором выводим сообщение msg.
Очевидно, для создания частей сообщения требуются операторы записи в переменную, а не logbook. Такие операторы есть в разделе General Commands.
Это:
1. <переменная> = sprintf: fmt=<строка формата>, <п1>, <п2>, <п3>, <п4>, <п5>
2. <переменная> = sprintf: pageid=<число>, textid=<число>, <п1>, <п2>, <п3>, <п4>, <п5>
Они имеют тот же формат, что и операторы записи в logbook, но помещают строку результата не в лог игрока, а в указанную переменную.
Чтобы красиво оформить помещаемую в logbook строку, можно использовать специальные символы и тэги форматирования.
Пока что мне известен один нормально, стабильно работающий спецсимвол - это перевод строки ('\n').
Тэгов форматирования значительно больше.
Простые тэги:
[b] текст [/b] - текст будет отображен желтым цветом;
текст - текст будет отображен ярко-белым цветом;
[green]текст[/green] - текст будет отображен зеленым цветом;
[red]текст[/red] - текст будет отображен красным цветом;
[justify]текст[/justify] - текст будет отформатирован так, чтобы иметь ровным как левый, так и правый край;
[center]текст[/center] - текст будет отцентрован;
[left]текст[/left] - текст будет иметь ровный левый край;
[right]текст[/right] - текст будет иметь ровный правый край.
Еще имеются два специальных тэга:
[author]автор[author] - в сообщении в поле "от кого" будет указана строка "автор". Этот тэг должен быть самым первым (и единственным) в теле сообщения.
[text cols='число колонок' colwidth='ширина колонки' colspacing='расстояние
между колонками']текст[/text] - если этот тэг присутствует, он должен идти сразу за тэгом [author] (если тэг [author] также присутствует) или в самом начале тела сообщения. Закрывающий тэг [/text] безусловно обрывает текст сообщения. Параметры cols, colwidth и colspacing могут присутствовать или отсутствовать независимо друг от друга. В чем задается ширина колонки и расстояние между колонками - неясно. В режиме экрана 640x480 эти числа, похоже, совпадают с пикселями экрана.
Возвращаясь к нашему скрипту, становится понятно, что оператор
052 write to player logbook: printf: pageid=2010 textid=70006, $Station, $min, $max, null, null
считывает из страницы с идентификатором 2010 строку с идентификатором 70006 и использует ее как строку формата, подставляя в нее три параметра - Station, min и max.
Эта строка определена там же, где и наши собственные команды StM - в файле 440003.xml:
<t id="70005">Station manager hired to station %s\nLow boundary of station account: %s\nTop boundary of station account: %s\n</t>
Зачем такие сложности, почему бы не записать эту строку прямо в тексте скрипта?
Во-первых, в благословенном встроенном редакторе скриптов вам просто не удастся набрать символ '[' чтобы внести в строку тэги форматирования.
Во-вторых, и это гораздо важнее, мы можем создать для нашего скрипта файл 700003.xml с такой строкой:
<t id="70005">На станцию %s нанят новый менеджер\nНижнее ограничение для счета станции составляет %s кредитов,\nверхнее ограничение составляет [b]%s[/b] кредитов\n</t>
Если пользователь игры выбирает русскую локализацию - вместо файлов 44nnnn.xml игра будет читать файлы 70nnnn.xml, и наш скрипт волшебным образом заговорит по-русски. То же самое касается других языков.
Правда, при запросе параметров скрипта все равно будут выводиться строки подсказки, "забитые" в файл скрипта. Так что, как ни крути, все равно для полной локализации придется переписывать сами скрипты. Но, если позволить себе такую же расслабуху, какую позволили себе авторы игры - то вполне сойдет и так.
Включение всех существующих станций в список управления. Сканирование галактики.
Скрипт StM.install.all включает в список управления все существующие на данный момент станции игрока, устанавливая им всем одинаковые пределы счета.
Аргументы:
min - число:нижний предел остатка на счете каждой станции;
max - число:верхний предел остатка на счете каждой станции.
Текст скрипта:
// Сканируем галактику по секторам - сначала по столбцам, затем - по строкам. Начинаем с нижнего правого угла карты.
001 $i = get max sectors in x direction
002 while $i >= 0
003
004 $j = get max sectors in y direction
005 while $j >= 0
// Для каждой пары координат {x,y} пытаемся получить сектор. Поскольку не каждая такая пара указывает на сектор - стоит проверять результат перед использованием. Если мы сейчас этого не сделаем - ничего страшного не произойдет, все равно список станций игрока окажется пустым, но лучше сразу завести себе такую полезную привычку.
007 $sec = get sector from universe index: x=$i, y=$j
008 if $sec -> exists
// Сектор существует - получаем список станций игрока в этом секторе. Список будет помещен в массив sts. Размер массива нам неизвестен, чтобы узнать его используем оператор size of array.
009 $sts = $sec -> get player owned station array from sector
010 $nst = size of array $sts
// Если переменная nst равна нулю - это означает что в секторе sec нет ни одной станции игрока. В этом случае цикл не будет исполнен ни разу. Оператор уменьшения dec nst стоит в самом начале цикла по той причине, что nst одновременно используется и как счетчик элементов массива, и как указатель на эти элементы. Поскольку элементы массива нумеруются начиная с нуля, индекс последнего элемента массива всегда на единицу меньше общего числа элементов. Раз уж мы сканируем массив от конца к началу - нам нужно уменьшать счетчик элементов ПЕРЕД тем, как применять его в качестве указателя на последний элемент массива.
011 while $nst
012 dec $nst =
// Получаем станцию игрока из списка станций игрока в секторе
013 $st = $sts[$nst]
// Для полученной станции вызываем рассмотренный ранее скрипт StM.install с параметром Station, указывающим на эту станцию.
014 @ = [THIS] -> call script 'StM.install' : Select station=$st Low boundary of station account=$min Top boundary of station account=$max
015 end
// Здесь завершается обработка существующего сектора
016 end
// Переходим к следующему индексу в столбце карты галактики.
018 dec $j =
019 end
// Переходим к следующему индексу в строке карты галактики.
021 dec $i =
022 end
023 return null
Увольнение менеджера со станции
Аргумент:
Station - объект:станция.
Текст скрипта:
// Блокируем глобальные переменные
001 @ = [THIS] -> call script 'ANS.lock' : LockName='StMlock' Pause=2000 Tries=0
// Считываем значения глобальных переменных
003 $StMnum = get global variable: name='StMnum'
004
005 $StMsts = get global variable: name='StMsts'
006 $StMjmMin = get global variable: name='StMjmMin'
007 $StMjmMax = get global variable: name='StMjmMax'
008 $StMtotal = get global variable: name='StMtotal'
009 $StMdiff = get global variable: name='StMdiff'
// Ищем в списке станций ту, которую нас попросили удалить. Не факт, что мы ее найдем. Если мы ее не найдем, переменная-счетчик i будет равна StMnum - таково первое условие выхода из цикла. Если найдем - i будет содержать индекс станции в массиве StMsts.
011 $i = 0
012 while $i < $StMnum
013 $st = $StMsts[$i]
// Второе условие выхода из цикла - если найдена нужная станция.
014 skip if $st != $Station
015 break
016 inc $i =
017 end
// Если станция найдена - удаляем ее из списка станций, удаляем ее параметры из списков параметров станций, уменьшаем общее количество станций в списке, записываем новые значения глобальных переменных, оставляем игроку соответствующее сообщение в логе.
019 if $i < $StMnum
020 remove element from array $StMsts at index $i
021 remove element from array $StMdiff at index $i
022 remove element from array $StMjmMax at index $i
023 remove element from array $StMjmMin at index $i
024 remove element from array $StMtotal at index $i
025
026 dec $StMnum =
027 set global variable: name='StMnum' value=$StMnum
028
029 set global variable: name='StMsts' value=$StMsts
030 set global variable: name='StMdiff' value=$StMdiff
031 set global variable: name='StMjmMax' value=$StMjmMax
032 set global variable: name='StMjmMin' value=$StMjmMin
033 set global variable: name='StMtotal' value=$StMtotal
034
035 write to player logbook: printf: pageid=2010 textid=70007, $Station, null, null, null, null
036 end
// В любом случае - снимаем блокировку с глобальных переменных.
038 set global variable: name='StMlock' value=[FALSE]
039 return null
Отчет о движении денег (StM.report.all)
Скрипт:
// Как обычно, начинаем с блокировки глобальных переменных
001 @ = [THIS] -> call script 'ANS.lock' : LockName='StMlock' Pause=2000 Tries=0
002
003 $StMsts = get global variable: name='StMsts'
004 $StMnum = get global variable: name='StMnum'
005 $StMtotal = get global variable: name='StMtotal'
006 $StMdiff = get global variable: name='StMdiff'
// Для красоты отчет будет иметь рамочку из сиволов '='. Здравствуйте, шестидесятые...
008 $rep = sprintf: pageid=2010 textid=70000, null, null, null, null, null
// В цикле проходим по списку станций. Начинаем с того, что показываем и запоминаем название сектора первой станции в списке. Для каждой последующей станции проверяем - совпадает ли название ее сектора с тем, который мы держим "в уме". Если не совпадает - показываем название нового сектора и запоминаем его в качестве текущего. И так далее...
010 $oldsec = null
// В переменной s будем накапливать общую сумму денег, переведенную за все время со всех станций.
011 $s = 0
// В переменной d будем накапливать общую сумму денег, переведенную со всех станций с момента выдачи последнего отчета (последнего перед этим).
012 $d = 0
// i - индекс очередной станции в списке.
013 $i = 0
014 while $i < $StMnum
015 $st = $StMsts[$i]
016
017 $newsec = $st -> get sector
018 if $newsec != $oldsec
019 $oldsec = $newsec
// Строка с идентификатором 70003 содержит шаблон для показа названия сектора.
020 $rl = sprintf: pageid=2010 textid=70002, $newsec, null, null, null, null
// Добавляем к отчету строчку с названием сектора.
021 $rep = $rep + $rl
022 end
// Здесь мы запоминаем сумму денег, переведенную со счета станции на счет игрока со времени последнего отчета - а затем обнуляем ее.
024 $total = $StMtotal[$i]
025 $diff = $StMdiff[$i]
026 $StMdiff[$i] = 0
027 $s = $s + $total
028 $d = $d + $diff
// Благословенная версия 1.4 позволяет нам (наконец-то!) показать имя станции.
030 $stname = $st -> get name
// Строка с идентификатором 70003 содержит шаблон для выдачи сведений по одной конкретной станции.
031 $rl = sprintf: pageid=2010 textid=70003, $stname, $total, $diff, null, null
// Добавляем к отчету строчку с данными по текущей станции.
032 $rep = $rep + $rl
// Переходим к следуюшей станции в списке.
034 inc $i =
035 end
// Под отчетом подводим красивую черту в стиле ретро - из символов '='.
037 $rl = sprintf: pageid=2010 textid=70000, null, null, null, null, null
038 $rep = $rep + $rl
// Отбиваем несколько переводов строки - чтобы в лог-файле отчеты не сливались.
039 $rl = sprintf: pageid=2010 textid=70004, null, null, null, null, null
040 $rep = $rep + $rl
// Сохраняем в глобальной переменной очищенный массив сумм, переведенных со времени последнего (теперь уже вот этого самого) отчета.
042 set global variable: name='StMdiff' value=$StMdiff
// Считываем из глобальной переменной дату предыдущего отчета и приводим ее к читаемому виду.
044 $since = get global variable: name='StMlastrep'
045 @ $since = [THIS] -> call script 'ANS.format.date' : ticks=$since
// Получаем и приводим к читаемому виду текущую игровую дату. Попутно сохраняем текущую дату в качестве даты последнего отчета (что соответствует действительности).
074 $date = playing time
048 set global variable: name='StMlastrep' value=$date
049 @ $date = [THIS] -> call script 'ANS.format.date' : ticks=$date
// Формируем заголовок отчета, включающий в себя текущую дату, дату предшествующего отчета, общую сумму денег за все время и сумму со времени предшествующего отчета. Текущую дату нужно включить, так как в лог-файле даты не проставляются (в отличие от записей в logbook игрока).
050 $head = sprintf: pageid=2010 textid=70001, $date, $since, $s, $d, null
// Разблокируем глобальные переменные - мы уже внесли все необходимые изменения.
052 set global variable: name='StMlock' value=[FALSE]
// Заголовок отчета пишем в logbook игрока.
054 write to player logbook $head
// К лог-файлу дописываем сначала заголовок, а затем и тело отчета.
055 write to log file #700 append=[TRUE] value=$head
056 write to log file #700 append=[TRUE] value=$rep
057 return null
Полный текст файла 440003.xml
<?xml version="1.0" encoding="UTF-8" ?>
<language
id="44">
<page id="2010" title="Commands" descr=" ">
<t id="1110">Hire manager to this station</t>
<t id="1111">Hire manager to all stations</t>
<t id="1112">Industry profit and loss statement</t>
<t id="1113">Set contigency fund</t>
<t id="1119">Fire manager from this station</t>
<t id="70000">=============================================================================\n</t>
<t id="70001">Industry Profit and Loss Statement\n\nDate: %15s\nSince: %15s\n\nTotal Profit: %15s\nSince Last Report: %15s\n\n</t>
<t id="70002">---- %-40s -------------------------------\n</t>
<t id="70003">%-43s %15s %15s\n</t>
<t id="70004">\n\n\n\n\n</t>
<t id="70005">Station manager hired to station %s\nLow boundary of station account: %s\nTop boundary of station account: %s\n</t>
<t id="70006">Set new account boundaries at station %s -\n%s for low and %s for top\n</t>
<t id="70007">Station manager fired from station %s\n</t>
</page>
<page id="2011" title="Commands" descr=" ">
<t id="1110">StM</t>
<t id="1111">StM</t>
<t id="1112">StM</t>
<t id="1113">StM</t>
<t id="1119">StM</t>
</page>
</language>
Последние замечания
Этот скрипт обладает одним врожденным недостатком. Вполне возможна такая ситуация, когда станции, стоящие в начале списка, выгребают со счета игрока всю наличность, и станциям в середине списка ничего не остается. В то же время на счетах станций в конце списка может оставаться излишек денег, который как раз пригодился бы для покрытия недостачи на счетах станций из середины списка.
Решение простое - делать подряд два прохода по списку станций. На первом проходе снимать излишки, на втором - восполнять недостачи. Но стоит ли овчинка выделки? Лично я считаю - не стоит. Впрочем, если вы заинтересовались описанием этого скрипта в учебных целях - вам есть смысл попробовать модифицировать его самостоятельно. Полезная тренировка.
Оглавление | Сообщение об ошибках | Ваше мнение о проекте | E-Mail автору |