This commit is contained in:
vvollo 2021-10-04 22:52:51 +03:00 committed by GitHub
parent bd274293aa
commit 4bb6186dc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 748 additions and 276 deletions

View File

@ -70,7 +70,8 @@ mp.shorten = {
["se"] = "southeast"; ["se"] = "southeast";
["sw"] = "southwest"; ["sw"] = "southwest";
["nw"] = "northwest"; ["nw"] = "northwest";
["u"] = "up";
["d"] = "down";
} }
mp.shorten_expert = { mp.shorten_expert = {
@ -87,8 +88,13 @@ function mp:skip_filter(w)
end end
return true return true
end end
function mp:ignore_filter(w)
_'@compass'.before_Default = 'Try to verb "go".' if w == 'the' or w == 'a' or w == 'an' then
return true
end
return false
end
_'@compass'.before_Default = function() p('"{#First}" is the direction. You can not ', mp.parsed[1], ' {#firstit}.') end
function mp.msg.SCORE(d) function mp.msg.SCORE(d)
if d > 0 then if d > 0 then
@ -98,19 +104,16 @@ function mp.msg.SCORE(d)
end end
end end
function mp.msg.MULTIDSC(oo, inv)
if #oo > 0 then
local s = oo[1]
if not s:hint'proper' and not s:hint'surname' then
p "the"
end
end
mp:multidsc(oo, inv)
end
mp.door.word = "door" mp.door.word = "door"
mp.msg.TITLE_SCORE = "Score: " mp.msg.TITLE_SCORE = function()
mp.msg.TITLE_TURNS = "Turns: " if mp.maxscore then
pr ("Score: ", mp.score, "/", mp.maxscore)
end
pr ("Score: ", mp.score)
end
mp.msg.TITLE_TURNS = function()
pr ("Turns: ", game:time() - 1)
end
mp.msg.YES = "Yes" mp.msg.YES = "Yes"
mp.msg.WHEN_DARK = "Darkness." mp.msg.WHEN_DARK = "Darkness."
mp.msg.UNKNOWN_THEDARK = "Probably, it is because there is no light?" mp.msg.UNKNOWN_THEDARK = "Probably, it is because there is no light?"
@ -118,6 +121,9 @@ mp.msg.COMPASS_NOWAY = "{#Me} can't go that way."
mp.msg.COMPASS_EXAM_NO = "Nothing interesting in that direction." mp.msg.COMPASS_EXAM_NO = "Nothing interesting in that direction."
mp.msg.ENUM = "items." mp.msg.ENUM = "items."
mp.msg.CUTSCENE_HELP = "Press <Enter> or enter {$fmt em|next} to continue." mp.msg.CUTSCENE_HELP = "Press <Enter> or enter {$fmt em|next} to continue."
if instead.reinstead then
mp.msg.CUTSCENE_MORE = "^{$fmt em|(more)}"
end
mp.msg.DLG_HELP = "Enter number to select the phrase." mp.msg.DLG_HELP = "Enter number to select the phrase."
mp.msg.NO_ALL = "This verb can not be used with all." mp.msg.NO_ALL = "This verb can not be used with all."
mp.msg.DROPPING_ALL = function(w) mp.msg.DROPPING_ALL = function(w)
@ -127,14 +133,14 @@ mp.msg.TAKING_ALL = function(w)
pn (iface:em("(taking "..w:the_noun()..")")) pn (iface:em("(taking "..w:the_noun()..")"))
end end
mp.msg.TAKE_BEFORE = function(w) mp.msg.TAKE_BEFORE = function(w)
pn (iface:em("(taking "..w:the_noun().." before)")) pn (iface:em("(taking "..w:the_noun().." first)"))
end end
mp.msg.DISROBE_BEFORE = function(w) mp.msg.DISROBE_BEFORE = function(w)
pn (iface:em("(disrobing "..w:the_noun().." before)")) pn (iface:em("(disrobing "..w:the_noun().." first)"))
end end
mp.msg.CLOSE_BEFORE = function(w) mp.msg.CLOSE_BEFORE = function(w)
pn (iface:em("(closing "..w:the_noun() .. " before)")) pn (iface:em("(closing "..w:the_noun() .. " first)"))
end end
local function str_split(str, delim) local function str_split(str, delim)
@ -340,10 +346,23 @@ end
mp.msg.enter = "<Enter>" mp.msg.enter = "<Enter>"
mp.msg.EMPTY = 'Excuse me?' mp.msg.EMPTY = 'Excuse me?'
mp.msg.UNKNOWN_VERB = "Unknown verb" mp.msg.UNKNOWN_VERB = function(w)
mp.msg.UNKNOWN_VERB_HINT = "Maybe you meant" p ("Unknown verb ", iface:em(w), ".")
end
mp.msg.UNKNOWN_VERB_HINT = function(w)
p ("The most similar word is ", iface:em(w), ".")
end
mp.msg.INCOMPLETE = "The sentence must be supplemented." mp.msg.INCOMPLETE = "The sentence must be supplemented."
mp.msg.INCOMPLETE_NOUN = "What do you want to apply the command to" mp.msg.INCOMPLETE_NOUN = function(w)
if w then
p ('What do you want to apply the command "'..w..'" to?')
else
p "What do you want to apply the command to?"
end
end
mp.msg.INCOMPLETE_SECOND_NOUN = function(w)
p ('Clarify the command: "', w, '"?')
end
mp.msg.UNKNOWN_OBJ = "Here is no such thing" mp.msg.UNKNOWN_OBJ = "Here is no such thing"
mp.msg.UNKNOWN_OBJ = function(w) mp.msg.UNKNOWN_OBJ = function(w)
if not w then if not w then
@ -363,9 +382,8 @@ mp.msg.UNKNOWN_WORD = function(w)
end end
mp.msg.NOTHING_OBJ = "Nothing." mp.msg.NOTHING_OBJ = "Nothing."
mp.msg.HINT_WORDS = "Maybe you meant" mp.msg.HINT_WORDS = "Maybe you meant"
mp.msg.HINT_OR = "or"
mp.msg.HINT_AND = "and"
mp.msg.AND = "and" mp.msg.AND = "and"
mp.msg.OR = "or"
mp.msg.MULTIPLE = "Here is" mp.msg.MULTIPLE = "Here is"
mp.msg.LIVE_ACTION = function(w) mp.msg.LIVE_ACTION = function(w)
p (mp:It(w), " would not like it.") p (mp:It(w), " would not like it.")
@ -374,11 +392,17 @@ mp.msg.NO_LIVE_ACTION = "{#Me} can only do that to something animate."
mp.msg.NOTINV = function(t) mp.msg.NOTINV = function(t)
p (lang.cap(t:the_noun()) .. " must be taken first.") p (lang.cap(t:the_noun()) .. " must be taken first.")
end end
mp.msg.WORN = function(_) mp.msg.HAS_WORN = function(_)
pr (" (worn)") return "worn"
end end
mp.msg.OPEN = function(_) mp.msg.HAS_OPEN = function(_)
pr (" (opened)") return "opened"
end
mp.msg.HAS_ON = function(_)
return "switched on"
end
mp.msg.HAS_LIGHT = function(_)
return "providing light"
end end
mp.msg.EXITBEFORE = "May be, {#me} should to {#if_has/#where,supporter,get off,get out of} {#thenoun/#where}." mp.msg.EXITBEFORE = "May be, {#me} should to {#if_has/#where,supporter,get off,get out of} {#thenoun/#where}."
@ -403,12 +427,34 @@ mp.msg.NOROOM = function(w)
end end
mp.msg.Exam.SWITCHSTATE = "{#Thefirst} {#is/#first} switched {#if_has/#first,on,on,off}." mp.msg.Exam.SWITCHSTATE = "{#Thefirst} {#is/#first} switched {#if_has/#first,on,on,off}."
mp.msg.Exam.NOTHING = "nothing." mp.msg.Exam.NOTHING = function(w)
mp.msg.Exam.IS = "there is" p "There is nothing "
mp.msg.Exam.ARE = "there are" if w:has 'supporter' then
mp.msg.Exam.IN = "In {#thefirst}" mp:pnoun (w, "on {#thefirst}.")
mp.msg.Exam.ON = "On {#thefirst}" else
mp:pnoun (w, "in {#thefirst}.")
end
end
mp.msg.Exam.CONTENT = function(w, oo)
local single = not oo[1]:hint 'plural'
if std.me():where() == w or std.here() == w then
p "{#Me} can see"
mp:multidsc(oo)
p " here."
return
end
if single then
p "There is"
else
p "There are"
end
mp:multidsc(oo)
if w:has 'supporter' then
mp:pnoun (w, " on {#thefirst}.")
else
mp:pnoun (w, " in {#thefirst}.")
end
end
mp.msg.Exam.DEFAULT = "{#Me} {#does/#me} not see anything unusual in {#thefirst}."; mp.msg.Exam.DEFAULT = "{#Me} {#does/#me} not see anything unusual in {#thefirst}.";
mp.msg.Exam.SELF = "{#Me} {#does/#me} not see anything unusual in {#yourself/#me}."; mp.msg.Exam.SELF = "{#Me} {#does/#me} not see anything unusual in {#yourself/#me}.";
@ -429,7 +475,8 @@ mp.msg.Walk.WALK = "But {#thefirst} {#is/#first} already here."
mp.msg.Walk.NOWHERE = "Where?" mp.msg.Walk.NOWHERE = "Where?"
mp.msg.Walk.INV = "{#Me} {#is/#me} holding this." mp.msg.Walk.INV = "{#Me} {#is/#me} holding this."
mp.msg.Enter.EXITBEFORE = "{#Me} {#present/#me,need} to {#if_has/#where,supporter,get off from,leave} {#thenoun/#where} first." mp.msg.Enter.EXITBEFORE = "{#Me} {#present/#me,need} to "..
"{#if_has/#where,supporter,get off from,leave} {#thenoun/#where} first."
mp.msg.Exit.NOTHERE = "But {#me} {#is/#me} not {#if_has/#first,supporter,on,in} {#thefirst}." mp.msg.Exit.NOTHERE = "But {#me} {#is/#me} not {#if_has/#first,supporter,on,in} {#thefirst}."
mp.msg.Exit.NOWHERE = "But {#me} {#have/#me} no way to exit." mp.msg.Exit.NOWHERE = "But {#me} {#have/#me} no way to exit."
@ -619,7 +666,8 @@ mp.msg.Answer.EMPTY = "{#Me} can't find anything to answer."
mp.msg.Answer.SELF = "Good answer." mp.msg.Answer.SELF = "Good answer."
mp.msg.Yes.YES = "That was a rhetorical question." mp.msg.Yes.YES = "That was a rhetorical question."
mp.msg.Buy.USE = "How exactly?" mp.msg.Buy.BUY = "Nothing is on sale."
mp.msg.Use.USE = "How exactly?"
mp.keyboard_space = '<space>' mp.keyboard_space = '<space>'
mp.keyboard_backspace = '<backspace>' mp.keyboard_backspace = '<backspace>'
@ -628,21 +676,21 @@ mp.msg.GAMEOVER_HELP = [[Use restart to restart game.]];
function mp:myself(ob) function mp:myself(ob)
if ob:hint'first' then if ob:hint'first' then
return { "myself", "me" } return { "myself", "self", "me" }
end end
if ob:hint'second' then if ob:hint'second' then
return { "yourself", "me", "myself" } return { "yourself", "myself", "self", "me" }
end end
if ob:hint'plural' then if ob:hint'plural' then
return { "themselves", "our" } return { "themselves", "ourselves", "self" }
end end
if ob:hint'female' then if ob:hint'female' then
return { "herself", "me" } return { "herself", "myself", "self", "me" }
end end
if ob:hint'male' then if ob:hint'male' then
return { "himself", "me" } return { "himself", "myself", "self", "me" }
end end
return { "itself" } return { "itself", "myself", "self", "me" }
end end
function mp:it(w) function mp:it(w)
@ -695,7 +743,8 @@ function mp:before_Enter(w)
return false return false
end end
mp.msg.HELP = [[{$fmt b|INSTRUCTIONS}^^ mp.msg.HELP = function()
p [[{$fmt b|INSTRUCTIONS}^^
Enter your actions in verb noun form. For example:^ Enter your actions in verb noun form. For example:^
> open door^ > open door^
@ -709,13 +758,27 @@ To examine whole scene, enter "exam" or press "Enter".^
^ ^
To exam your inventory, enter "inv".^ To exam your inventory, enter "inv".^
^ ^
Use compass directions to walk. For example: "go north" or "north" or just "n". Use compass directions to walk. For example: "go north" or "north" or just "n". There are also up and down directions, outside and inside.]]
^^ if not instead.tiny then
You may use the "TAB" key for autocompletion. p [[^^You may use the "TAB" key for autocompletion.]]
]] else
p [[^^Use "save" and "load" to save and load game.]]
if instead.tiny then
p [[For ex. "save 1".]]
end
p [[Restart game: "restart".]]
if instead.reinstead then
p [[^^Also available: !restart, !quit, !info, !save, !load and !font <size>.]]
end
end
end
function mp.token.compass1(_) function mp.token.compass1(_)
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass" return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|"..
"{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|"..
"{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|"..
"{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
end end
function mp.token.compass2(_) function mp.token.compass2(_)
@ -724,12 +787,18 @@ end
std.mod_init(function(_) std.mod_init(function(_)
Verb { "#Walk", Verb { "#Walk",
"go,walk,run,enter", "go,walk,run,enter,come",
"{compass1} : Walk", "{compass1} : Walk",
"in|into|inside|on {noun}/scene,enterable : Enter", "in|into|inside|on {noun}/scene,enterable : Enter",
"{noun}/scene : Walk", "{noun}/scene : Walk",
"{compass2}: Walk", "{compass2}: Walk",
"outside|out|away: Exit" } "outside|out|away: Exit"
}
Verb { "#Enter",
"enter",
"{noun}/scene,enterable : Enter"
}
Verb { "#Sit", Verb { "#Sit",
"sit,stand", "sit,stand",
@ -740,7 +809,7 @@ Verb { "#Lie",
"down in|into|inside|on {noun}/scene,enterable : Enter" } "down in|into|inside|on {noun}/scene,enterable : Enter" }
Verb { "#Exit", Verb { "#Exit",
"exit,out", "exit,out,leave",
"?from {noun}/scene : Exit", "?from {noun}/scene : Exit",
": Exit"} ": Exit"}
@ -751,6 +820,7 @@ Verb { "#Exam",
"inventory : Inv", "inventory : Inv",
"~ under {noun} : LookUnder", "~ under {noun} : LookUnder",
"~ in|inside|into|through|on {noun} : Search", "~ in|inside|into|through|on {noun} : Search",
"~ ?at {noun} : Exam",
"~ up * in {noun} : Consult reverse", "~ up * in {noun} : Consult reverse",
} }
@ -853,6 +923,7 @@ Verb {
Verb { Verb {
"#SwitchOff", "#SwitchOff",
"switch",
"off {noun}: SwitchOff", "off {noun}: SwitchOff",
"~ {noun} off : SwitchOff", "~ {noun} off : SwitchOff",
} }
@ -979,7 +1050,7 @@ Verb {
Verb { Verb {
"#Listen", "#Listen",
"listen.hear", "listen,hear",
"Listen", "Listen",
"?to {noun}: Listen", "?to {noun}: Listen",
} }
@ -1090,8 +1161,7 @@ Verb {
Verb { Verb {
"#Talk", "#Talk",
"talk", "talk",
"with {noun}/live : Talk" "with|to {noun}/live : Talk"
} }
Verb { Verb {
@ -1180,6 +1250,8 @@ MetaVerb {
"~parser", "~parser",
"expert on : MetaExpertOn", "expert on : MetaExpertOn",
"expert off : MetaExpertOff", "expert off : MetaExpertOff",
"verbs : MetaVerbs",
"version : MetaVersion",
} }
MetaVerb { MetaVerb {
@ -1219,6 +1291,12 @@ std.mod_start(function()
"MetaUndo", "MetaUndo",
} }
end end
if mp.score then
MetaVerb {
"~ счёт",
"MetaScore",
}
end
end) end)
-- Dialog -- Dialog
std.phr.default_Event = "Exam" std.phr.default_Event = "Exam"

View File

@ -67,7 +67,40 @@ function mp:skip_filter(w)
return true return true
end end
_'@compass'.before_Default = 'Попробуйте глагол "идти".' local function endswith(w, t)
return not not w:find(t..'$')
end
function mp:verb_filter(w)
if #w > 1 then
return true
end
local utf = mp.utf
local verb = w[1]
local t = utf.chars(w[1])
if endswith(verb, 'ся') or endswith(verb, 'сь') or endswith(verb, 'те') then
local len = #verb
len = len - utf.bb(verb, len)
len = len - utf.bb(verb, len)
verb = verb:sub(1, len)
end
if endswith(verb, 'и') or endswith(verb, 'ь') then
return true
end
local t = utf.chars(verb)
local a = { ['а'] = true, ['е'] = true, ['и'] = true,
['о'] = true, ['у'] = true, ['ы'] = true,
['ю'] = true, ['я'] = true };
local len = #t
if len >= 2 and a[t[len - 1]] and t[len] == 'й' then -- or a[t[len]] then
return true
end
return false
end
_'@compass'.before_Default = function()
p('"{#First}" это направление. {#Firstit/вн} нельзя ', mp.parsed[1], ".")
end
function mp.msg.SCORE(d) function mp.msg.SCORE(d)
if d > 0 then if d > 0 then
@ -77,8 +110,16 @@ function mp.msg.SCORE(d)
end end
end end
mp.door.word = -"дверь"; mp.door.word = -"дверь";
mp.msg.TITLE_SCORE = "Счёт: " mp.msg.TITLE_SCORE = function()
mp.msg.TITLE_TURNS = "Ходы: " if mp.maxscore then
pr ("Счёт: ", mp.score, "/", mp.maxscore)
else
pr ("Счёт: ", mp.score)
end
end
mp.msg.TITLE_TURNS = function()
pr ("Ходы: ", game:time() - 1)
end
mp.msg.YES = "Да" mp.msg.YES = "Да"
mp.msg.WHEN_DARK = "Кромешная тьма." mp.msg.WHEN_DARK = "Кромешная тьма."
mp.msg.UNKNOWN_THEDARK = "Возможно, это потому что в темноте ничего не видно?" mp.msg.UNKNOWN_THEDARK = "Возможно, это потому что в темноте ничего не видно?"
@ -86,6 +127,9 @@ mp.msg.COMPASS_NOWAY = "Этот путь недоступен."
mp.msg.COMPASS_EXAM_NO = "В этом направлении не видно ничего примечательного." mp.msg.COMPASS_EXAM_NO = "В этом направлении не видно ничего примечательного."
mp.msg.ENUM = "шт." mp.msg.ENUM = "шт."
mp.msg.CUTSCENE_HELP = "Для продолжения нажмите <ввод> или введите {$fmt em|дальше}." mp.msg.CUTSCENE_HELP = "Для продолжения нажмите <ввод> или введите {$fmt em|дальше}."
if instead.reinstead then
mp.msg.CUTSCENE_MORE = "^{$fmt em|(дальше)}"
end
mp.msg.DLG_HELP = "Для выбора фразы введите цифру." mp.msg.DLG_HELP = "Для выбора фразы введите цифру."
mp.msg.NO_ALL = "Это действие нельзя применить на всё." mp.msg.NO_ALL = "Это действие нельзя применить на всё."
mp.msg.DROPPING_ALL = function(w) mp.msg.DROPPING_ALL = function(w)
@ -130,11 +174,25 @@ end
mp.msg.enter = "<ввод>" mp.msg.enter = "<ввод>"
mp.msg.EMPTY = 'Простите?' mp.msg.EMPTY = 'Простите?'
mp.msg.UNKNOWN_VERB = "Непонятный глагол" mp.msg.UNKNOWN_VERB = function(w)
mp.msg.UNKNOWN_VERB_HINT = "Возможно, вы имели в виду" p ("Непонятный глагол ", iface:em(w), ".")
end
mp.msg.UNKNOWN_VERB_HINT = function(w)
p ("Самое похожее слово: ", iface:em(w), ".")
end
mp.msg.INCOMPLETE = "Нужно дополнить предложение." mp.msg.INCOMPLETE = "Нужно дополнить предложение."
mp.msg.INCOMPLETE_NOUN = "К чему вы хотите применить команду" mp.msg.INCOMPLETE_NOUN = function(w)
mp.msg.INCOMPLETE_SECOND_NOUN = "Уточните команду:" if w then
p('К чему вы хотите применить команду "',w, '"?')
else
p"К чему вы хотите применить команду?"
end
end
mp.msg.INCOMPLETE_SECOND_NOUN = function(w)
p ('Уточните команду: "',w,'"?')
end
mp.msg.UNKNOWN_OBJ = function(w) mp.msg.UNKNOWN_OBJ = function(w)
if not w then if not w then
p "Об этом предмете ничего не известно." p "Об этом предмете ничего не известно."
@ -152,10 +210,9 @@ mp.msg.UNKNOWN_WORD = function(w)
p ("(",w,"?).") p ("(",w,"?).")
end end
end end
mp.msg.HINT_WORDS = "Может быть" mp.msg.HINT_WORDS = "Возможно"
mp.msg.HINT_OR = "или"
mp.msg.HINT_AND = "и"
mp.msg.AND = "и" mp.msg.AND = "и"
mp.msg.OR = "или"
mp.msg.MULTIPLE = "Тут есть" mp.msg.MULTIPLE = "Тут есть"
mp.msg.LIVE_ACTION = function(w) mp.msg.LIVE_ACTION = function(w)
p (w:It'дт'," это не понравится.") p (w:It'дт'," это не понравится.")
@ -166,16 +223,28 @@ mp.msg.NOTINV = function(t)
p (lang.cap(t:noun'вн') .. " сначала нужно взять.") p (lang.cap(t:noun'вн') .. " сначала нужно взять.")
end end
--"надет" --"надет"
mp.msg.WORN = function(w) mp.msg.HAS_WORN = function(w)
local hint = w:gram().hint local hint = w:gram().hint
pr (" (",mp.mrd:word('надет/' .. hint), ")") return mp.mrd:word('надет/' .. hint)
end end
--"открыт" --"открыт"
mp.msg.OPEN = function(w) mp.msg.HAS_OPEN = function(w)
local hint = w:gram().hint local hint = w:gram().hint
pr (" (",mp.mrd:word('открыт/' .. hint), ")") return mp.mrd:word('открыт/' .. hint)
end end
mp.msg.EXITBEFORE = "Возможно, {#me/дт} нужно сначала {#if_has/#where,supporter,слезть с {#where/рд}.,покинуть {#where/вн}.}" --"включён"
mp.msg.HAS_ON = function(w)
local hint = w:gram().hint
return mp.mrd:word('включён/' .. hint)
end
--"светится"
mp.msg.HAS_LIGHT = function(w)
local hint = w:gram().hint
return mp.mrd:word('светится/' .. hint)
end
mp.msg.EXITBEFORE = "Возможно, {#me/дт} нужно сначала "..
"{#if_has/#where,supporter,слезть с {#where/рд}.,покинуть {#where/вн}.}"
mp.default_Event = "Exam" mp.default_Event = "Exam"
mp.default_Verb = "осмотреть" mp.default_Verb = "осмотреть"
@ -186,7 +255,6 @@ mp.msg.ACCESS2 = "{#Second} отсюда не{#word/доступен,#second}."
mp.msg.Look.HEREIS = "Здесь находится" mp.msg.Look.HEREIS = "Здесь находится"
mp.msg.Look.HEREARE = "Здесь находятся" mp.msg.Look.HEREARE = "Здесь находятся"
mp.msg.NOROOM = function(w) mp.msg.NOROOM = function(w)
if w == std.me() then if w == std.me() then
p ("У {#me/рд} слишком много вещей.") p ("У {#me/рд} слишком много вещей.")
@ -199,11 +267,47 @@ end
--"включён" --"включён"
--"выключен" --"выключен"
mp.msg.Exam.SWITCHSTATE = "{#First} сейчас {#if_has/#first,on,{#word/включён,#first},{#word/выключен,#first}}." mp.msg.Exam.SWITCHSTATE = "{#First} сейчас {#if_has/#first,on,{#word/включён,#first},{#word/выключен,#first}}."
mp.msg.Exam.NOTHING = "ничего нет."
mp.msg.Exam.IS = "находится" mp.msg.Exam.NOTHING = function(w)
mp.msg.Exam.ARE = "находятся" if w:has 'supporter' then
mp.msg.Exam.IN = "В {#first/пр,2}" mp:pnoun (w, "На {#first/пр,2}")
mp.msg.Exam.ON = "На {#first/пр,2}" else
mp:pnoun (w, "В {#first/пр,2}")
end
p "ничего нет."
end
mp.msg.Exam.CONTENT = function(w, oo)
local single = #oo == 1 and not oo[1]:hint 'plural'
if std.me():where() == w or std.here() == w then
if false then
if single then
p "Здесь находится"
else
p "Здесь находятся"
end
mp:multidsc(oo)
else
p "{#Me} {#word/видеть,#me,нст} здесь";
mp:multidsc(oo, 'вн')
end
p "."
return
end
if w:has 'supporter' then
mp:pnoun (w, "На {#first/пр,2}")
else
mp:pnoun (w, "В {#first/пр,2}")
end
if single then
p "находится"
else
p "находятся"
end
mp:multidsc(oo)
p "."
end
--"видеть" --"видеть"
mp.msg.Exam.DEFAULT = "{#Me} не {#word/видеть,#me,нст} {#vo/{#first/пр}} ничего необычного."; mp.msg.Exam.DEFAULT = "{#Me} не {#word/видеть,#me,нст} {#vo/{#first/пр}} ничего необычного.";
mp.msg.Exam.SELF = "{#Me} не {#word/видеть,#me,нст} в себе ничего необычного."; mp.msg.Exam.SELF = "{#Me} не {#word/видеть,#me,нст} в себе ничего необычного.";
@ -240,7 +344,8 @@ mp.msg.Exit.CLOSED = "Но {#first} {#word/закрыт,#first}."
--"покидать" --"покидать"
--"слезать" --"слезать"
mp.msg.Exit.EXITED = "{#Me} {#if_has/#first,supporter,{#word/слезать с,#me,нст} {#first/рд},{#word/покидать,#me,нст} {#first/вн}}." mp.msg.Exit.EXITED = "{#Me} {#if_has/#first,supporter,{#word/слезать,#me,нст} {#so/{#first/рд}},"..
"{#word/покидать,#me,нст} {#first/вн}}."
mp.msg.GetOff.NOWHERE = "Но {#me/дт} не с чего слезать." mp.msg.GetOff.NOWHERE = "Но {#me/дт} не с чего слезать."
@ -295,7 +400,10 @@ mp.msg.Take.WORN = "{#First} {#word/надет,#first} на {#firstwhere/вн}."
mp.msg.Take.PARTOF = "{#First} {#if_hint/#first,plural,являются,является} частью {#firstwhere/рд}." mp.msg.Take.PARTOF = "{#First} {#if_hint/#first,plural,являются,является} частью {#firstwhere/рд}."
mp.msg.Remove.WHERE = "{#First} не {#word/находиться,#first,нст} {#if_has/#second,supporter,на,в} {#second/пр,2}." mp.msg.Remove.WHERE = "{#First} не {#word/находиться,#first,нст} {#if_has/#second,supporter,на,в} {#second/пр,2}."
mp.msg.Remove.REMOVE = "{#First} {#if_has/#second,supporter,{#word/поднят с,#first},{#word/извлечён из,#first}} {#second/рд}." --"поднят"
--"извлечён"
mp.msg.Remove.REMOVE = "{#First} {#if_has/#second,supporter,{#word/поднят с,#first},"..
"{#word/извлечён из,#first}} {#second/рд}."
mp.msg.Drop.SELF = "У {#me/рд} не хватит ловкости." mp.msg.Drop.SELF = "У {#me/рд} не хватит ловкости."
mp.msg.Drop.WORN = "{#First/вн} сначала нужно снять." mp.msg.Drop.WORN = "{#First/вн} сначала нужно снять."
@ -480,13 +588,13 @@ end
function mp:myself(_, hint) function mp:myself(_, hint)
local ww = dict({ local ww = dict({
["вн"] = "себя"; ["вн"] = { "себя" };
["дт"] = "себе"; ["дт"] = { "себе" };
["тв"] = "собой"; ["тв"] = {"собой" };
["пр"] = "себе"; ["пр"] = { "себе" };
["рд"] = "себя"; ["рд"] = { "себя" };
}, hint) }, hint)
return { ww } return ww
end end
function mp:it(w, hint) function mp:it(w, hint)
@ -560,27 +668,65 @@ function mp:err_noun(noun)
end end
function mp.shortcut.vo(hint) function mp.shortcut.vo(hint)
local w = std.split(mp.mrd.lang.norm(hint))
local utf = mp.utf
local vow = lang.is_vowel
local char = utf.char
local excl = {
["льве"] = true,
["львах"] = true,
["льду"] = true,
["льдах"] = true,
["льне"] = true,
["льнах"] = true,
["лбу"] = true,
["лбах"] = true,
["лжи"] = true,
["лжах"] = true,
["мху"] = true,
["мхах"] = true,
["рву"] = true,
["рвах"] = true,
["ржи"] = true,
["ржах"] = true,
["рту"] = true,
["ртах"] = true,
["мне"] = true,
["что"] = true,
}
w = w[#w]
if mp.utf.len(w) > 2 and
(vow(char(w, 1) == 'в' or vow(char(w, 1) == 'ф') and
not vow(char(w, 2)))) or excl[w] then
return "во ".. hint
end
return "в ".. hint return "в ".. hint
-- local w = std.split(hint)
-- w = w[#w]
-- if mp.utf.len(w) > 2 and
-- (lang.is_vowel(utf.char(w, 1)) or
-- lang.is_vowel(utf.char(w, 2))) then
-- return "в ".. hint
-- end
-- return "во ".. hint
end end
function mp.shortcut.so(hint) function mp.shortcut.so(hint)
local so = {
["с"] = true,
["з"] = true,
["ш"] = true,
["ж"] = true,
["л"] = true,
["р"] = true,
["м"] = true,
}
local w = std.split(mp.mrd.lang.norm(hint))
local utf = mp.utf
w = w[#w]
if utf.len(w) > 2 and
((so[utf.char(w, 1)] and
not lang.is_vowel(utf.char(w, 2))) or utf.char(w, 1) == 'щ') then
return "со ".. hint
end
if utf.len(w) > 2 and utf.char(w, 1) == 'л' and utf.char(w, 2) == 'ь' and
not lang.is_vowel(utf.char(w, 2)) then
return "со ".. hint
end
return "с ".. hint return "с ".. hint
-- local w = std.split(hint)
-- w = w[#w]
-- if mp.utf.len(w) > 2 and
-- (lang.is_vowel(utf.char(w, 1)) or
-- lang.is_vowel(utf.char(w, 2))) then
-- return "с ".. hint
-- end
-- return "со ".. hint
end end
function mp:before_Enter(w) function mp:before_Enter(w)
@ -591,7 +737,8 @@ function mp:before_Enter(w)
return false return false
end end
mp.msg.HELP = [[{$fmt b|КАК ИГРАТЬ?}^^ mp.msg.HELP = function()
p [[{$fmt b|КАК ИГРАТЬ?}^^
Вводите ваши действия в виде простых предложений вида: глагол -- существительное. Например:^ Вводите ваши действия в виде простых предложений вида: глагол -- существительное. Например:^
> открыть дверь^ > открыть дверь^
@ -607,14 +754,27 @@ mp.msg.HELP = [[{$fmt b|КАК ИГРАТЬ?}^^
^ ^
Чтобы узнать какие предметы у вас с собой, наберите "инвентарь" или "инв".^ Чтобы узнать какие предметы у вас с собой, наберите "инвентарь" или "инв".^
^ ^
Для перемещений используйте стороны света, например: "идти на север" или "север" или просто "с".^ Для перемещений используйте стороны света, например: "идти на север" или "север" или просто "с". Кроме сторон света можно перемещаться вверх ("вверх" или "вв") и вниз ("вниз" или "вн"), "внутрь" и "наружу".]]
Кроме сторон света можно перемещаться вверх ("вверх" или "вв") и вниз ("вниз" или "вн"). if not instead.tiny then
^^ p [[^^Вы можете воспользоваться клавишей "TAB" для автодополнения ввода.]]
Вы можете воспользоваться клавишей "TAB" для автодополнения ввода. else
]] p [[^^Вы можете сокращать названия объектов.]]
p [[^^Чтобы сохранять и загружать игру используйте "сохранить" и "загрузить".]]
if instead.tiny then
p [[Например, "сохранить 1".]]
end
p [[Начать заново: "заново".]]
if instead.reinstead then
p [[^^Также доступны команды: !restart, !quit, !info, !save, !load и !font <размер>.]]
end
end
end
function mp.token.compass1(_) function mp.token.compass1(_)
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass" return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|"..
"{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|"..
"{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|"..
"{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
end end
function mp.token.compass2(_) function mp.token.compass2(_)
@ -659,8 +819,8 @@ Verb { "#Exam",
"~ в|во|на {noun}/пр,2 : Search", "~ в|во|на {noun}/пр,2 : Search",
"~ внутри {noun}/рд : Search", "~ внутри {noun}/рд : Search",
"~ в|во {noun}/вн : Search", "~ в|во {noun}/вн : Search",
"~ в|во {noun}/пр,2 о|об|обо|про * : Consult", "~ в|во {noun}/пр,2 ?о|?об|?обо|?про * : Consult",
"~ о|об|обо|про * в|во {noun}/пр,2 : Consult reverse", "~ ?о|?об|?обо|?про * в|во {noun}/пр,2 : Consult reverse",
} }
Verb { "#Search", Verb { "#Search",
@ -668,8 +828,8 @@ Verb { "#Search",
"{noun}/вн : Search", "{noun}/вн : Search",
"в|во|на {noun}/пр,2 : Search", "в|во|на {noun}/пр,2 : Search",
"под {noun}/тв : LookUnder", "под {noun}/тв : LookUnder",
"~ в|во {noun}/пр,2 * : Consult", "~ в|во {noun}/пр,2 ?о|?об|?обо|?про * : Consult",
"~ * в|во {noun}/пр,2 : Consult reverse", "~ ?о|?об|?обо|?про * в|во {noun}/пр,2 : Consult reverse",
} }
Verb { "#Open", Verb { "#Open",
@ -721,7 +881,7 @@ Verb { "#Take",
} }
Verb { "#Insert", Verb { "#Insert",
"воткн/уть,втык/ать,вставить,влож/ить", "воткн/уть,втык/ать,вставить,влож/ить,"..
"[|про|за]сун/уть,вставь/", "[|про|за]сун/уть,вставь/",
"{noun}/вн,held в|во {noun}/вн,inside : Insert", "{noun}/вн,held в|во {noun}/вн,inside : Insert",
"~ {noun}/вн,held внутрь {noun}/рд : Insert", "~ {noun}/вн,held внутрь {noun}/рд : Insert",
@ -742,7 +902,7 @@ Verb { "#Drop",
Verb { Verb {
"#ThrowAt", "#ThrowAt",
"брос/ить,выбро/сить,кин/уть,кида/ть,швыр/нуть,метн/уть,метать", "брос/ить,выбро/сить,кину/ть,кинь/,кида/ть,швыр/нуть,метн/уть,метать",
"{noun}/вн,held : Drop", "{noun}/вн,held : Drop",
"{noun}/вн,held в|во|на {noun}/вн : ThrowAt", "{noun}/вн,held в|во|на {noun}/вн : ThrowAt",
"~ в|во|на {noun}/вн {noun}/вн : ThrowAt reverse", "~ в|во|на {noun}/вн {noun}/вн : ThrowAt reverse",
@ -1098,6 +1258,26 @@ Verb {
} }
if DEBUG then if DEBUG then
function mp:MetaForm(w)
if not w then return end
local t, hint
w = w:gsub("_", "/")
if w:find "/" then
hint = true
end
for _, f in ipairs { "им", "рд", "дт", "вн", "тв", "пр", "пр,2" } do
local ww = w
if hint then
ww = ww .. ','.. f
else
ww = ww .. '/' .. f
end
t = self.mrd:word(ww)
pn(t, " (", f, ")")
end
end
MetaVerb { MetaVerb {
"#MetaWord", "#MetaWord",
"~_слово", "~_слово",
@ -1119,6 +1299,11 @@ if DEBUG then
"~_дамп", "~_дамп",
"MetaDump" "MetaDump"
} }
MetaVerb {
"#МетаForm",
"~_форм/ы",
"* :MetaForm"
}
end end
MetaVerb { MetaVerb {
"#MetaTranscript", "#MetaTranscript",
@ -1133,6 +1318,8 @@ MetaVerb {
"~парсер", "~парсер",
"эксперт да : MetaExpertOn", "эксперт да : MetaExpertOn",
"эксперт нет : MetaExpertOff", "эксперт нет : MetaExpertOff",
"глаголы : MetaVerbs",
"версия : MetaVersion",
} }
MetaVerb { MetaVerb {
@ -1173,10 +1360,16 @@ std.mod_start(function()
mp.msg.MetaUndo.EMPTY = "Отменять нечего." mp.msg.MetaUndo.EMPTY = "Отменять нечего."
MetaVerb { MetaVerb {
"#MetaUndo", "#MetaUndo",
"~отмен/ить", "~отмен/а",
"MetaUndo", "MetaUndo",
} }
end end
if mp.score then
MetaVerb {
"~ счёт",
"MetaScore",
}
end
end) end)
-- Dialog -- Dialog
std.phr.default_Event = "Exam" std.phr.default_Event = "Exam"

View File

@ -1,5 +1,5 @@
local curdir = std.getinfo(1).source:gsub("^(.+[\\/])[^\\/]+$", "%1"):gsub("^@", ""); local curdir = std.getinfo(1).source:gsub("^(.+[\\/])[^\\/]+$", "%1"):gsub("^@", "");
local table = std.table
require "fmt" require "fmt"
require "snapshots" require "snapshots"
@ -97,6 +97,38 @@ local function utf_chars(b)
return res return res
end end
local function utf_similar(str1, str2, lev)
local chars1 = utf_chars(str1)
local chars2 = utf_chars(str2)
local len1 = #chars1
local len2 = #chars2
if len1 < lev or len2 < lev then
return false
end
for i = 0, len2 - lev do
local ok = true
for k = 1, lev do
if chars1[k] ~= chars2[i + k] then
ok = false
break
end
end
if ok then return true end
end
for i = 0, len1 - lev do
local ok = true
for k = 1, lev do
if chars2[k] ~= chars1[i + k] then
ok = false
break
end
end
if ok then return true end
end
return false
end
--- Returns the Levenshtein distance between the two given strings. --- Returns the Levenshtein distance between the two given strings.
-- https://gist.github.com/Badgerati/3261142 -- https://gist.github.com/Badgerati/3261142
@ -223,8 +255,11 @@ end
mp = std.obj { mp = std.obj {
nam = '@metaparser'; nam = '@metaparser';
started = false;
score = false; score = false;
maxscore = false;
expert_mode = true; expert_mode = true;
strict_mode = false;
autohelp = false; autohelp = false;
autohelp_limit = 1000; autohelp_limit = 1000;
autohelp_noverbs = false; autohelp_noverbs = false;
@ -237,7 +272,7 @@ mp = std.obj {
detailed_inv = false; detailed_inv = false;
daemons = std.list {}; daemons = std.list {};
{ {
version = "1.11"; version = "2.2.2";
cache = { tokens = {}, nouns = false }; cache = { tokens = {}, nouns = false };
scope = std.list {}; scope = std.list {};
logfile = false; logfile = false;
@ -254,6 +289,7 @@ mp = std.obj {
ff = std.rawget(_G, 'utf8_next') or utf_ff; ff = std.rawget(_G, 'utf8_next') or utf_ff;
len = std.rawget(_G, 'utf8_len') or utf_len; len = std.rawget(_G, 'utf8_len') or utf_len;
char = std.rawget(_G, 'utf8_char') or utf_char; char = std.rawget(_G, 'utf8_char') or utf_char;
chars = utf_chars;
}; };
lev_thresh = 3; lev_thresh = 3;
lev_ratio = 0.20; lev_ratio = 0.20;
@ -611,24 +647,29 @@ function mp.token.noun(w)
for _, o in ipairs(oo) do for _, o in ipairs(oo) do
local d = {} local d = {}
local r = o:noun(attr, d) local r = o:noun(attr, d)
for k, v in ipairs(d) do if o == std.me() and mp.myself then
local hidden = (k ~= 1) or w.hidden for _, vm in ipairs(mp:myself(o, w.morph) or {}) do
if o:has 'multi' then table.insert(ww, { optional = w.optional, word = vm, morph = attr, ob = o, alias = o.alias,
hidden = w.hidden or (v.idx ~= 1) hidden = w.hidden or _ ~= 1 })
end
if o == std.me() and mp.myself then
for _, vm in ipairs(mp:myself(o, w.morph)) do
table.insert(ww, { optional = w.optional, word = vm, morph = attr, ob = o, alias = o.alias, hidden = hidden })
end
break
else
table.insert(ww, { optional = w.optional, word = r[k], ob = o, morph = attr, alias = v.alias, hidden = hidden })
end end
end end
if o == mp.first_it then if o ~= std.me() or (not o:hint'first' and not o:hint'second') then
table.insert(syms, 1, o) for k, v in ipairs(d) do
elseif o == mp.second_it then local hidden = (k ~= 1) or w.hidden
table.insert(syms, o) if o:has 'multi' then
hidden = w.hidden or (v.idx ~= 1)
end
table.insert(ww,
{ optional = w.optional,
word = r[k], ob = o,
morph = attr, alias = v.alias,
hidden = hidden })
end
if o == mp.first_it then
table.insert(syms, 1, o)
elseif o == mp.second_it then
table.insert(syms, o)
end
end end
end end
@ -689,6 +730,11 @@ function mp:eq(t1, t2, lev)
return self:__startswith(t2, t) return self:__startswith(t2, t)
end end
if lev then if lev then
t1 = self:norm(t1)
t2 = self:norm(t2)
if not utf_similar(t1, t2, 3) then -- 3 is hardcoded
return false
end
local l = utf_lev(t1, t2) local l = utf_lev(t1, t2)
if l < lev and l / (utf_len(t1) + utf_len(t2)) <= self.lev_ratio then if l < lev and l / (utf_len(t1) + utf_len(t2)) <= self.lev_ratio then
return l return l
@ -708,7 +754,7 @@ end
function mp:pattern(t, delim) function mp:pattern(t, delim)
local words = {} local words = {}
local pat = str_split(self:norm(t), delim or "|") local pat = str_split(t, delim or "|")
for _, v in ipairs(pat) do for _, v in ipairs(pat) do
local w = { } local w = { }
local ov = v local ov = v
@ -939,6 +985,14 @@ function mp:skip_filter()
return true return true
end end
function mp:ignore_filter(w)
return false
end
function mp:verb_filter(words)
return true
end
function mp:lookup_verb(words, lev) function mp:lookup_verb(words, lev)
local ret = {} local ret = {}
local w = self:verbs() local w = self:verbs()
@ -947,9 +1001,20 @@ function mp:lookup_verb(words, lev)
for _, vv in ipairs(v.verb) do for _, vv in ipairs(v.verb) do
local verb = vv.word .. (vv.morph or "") local verb = vv.word .. (vv.morph or "")
local i, len, rlev local i, len, rlev
i, len, rlev = word_search(words, verb, lev and self.lev_thresh) local vwords = mp.strict_mode and { words[1] } or words
if not i and not lev and verb ~= vv.word then i, len, rlev = word_search(vwords, verb, lev and self.lev_thresh)
i, len = self:lookup_short(words, vv.word) if not i and not lev and vv.morph then
i, len = self:lookup_short(vwords, vv.word)
if i then
local v = {}
for k = i, i + len - 1 do
table.insert(v, words[k])
end
if verb:find(table.concat(v, ' '), 1, true) ~= 1 and
not self:verb_filter(v) then
i = false
end
end
end end
if i and i > 1 and not self:skip_filter({words[i - 1]}) then if i and i > 1 and not self:skip_filter({words[i - 1]}) then
i = nil i = nil
@ -1194,11 +1259,12 @@ function mp:compl_filter(v)
hidden = not v.ob.hint_noun hidden = not v.ob.hint_noun
end end
end end
if hidden and self.compl_thresh == 0 then local _, pre = self:compl_ctx()
local nsym = mp.utf.len(pre)
if hidden and self.compl_thresh == 0 and nsym == 0 then
return false return false
end end
local _, pre = self:compl_ctx() if nsym < self.compl_thresh then
if mp.utf.len(pre) < self.compl_thresh then
return false return false
end end
if not v.ob or not v.morph then if not v.ob or not v.morph then
@ -1304,6 +1370,7 @@ function mp:compl_ctx_poss()
table.insert(res, v) table.insert(res, v)
end end
end end
res.eol = ctx.eol
return res return res
end end
@ -1318,6 +1385,7 @@ function mp:compl(str)
collectgarbage("stop") collectgarbage("stop")
self:compl_ctx_current(); self:compl_ctx_current();
poss = self:compl_ctx_poss() poss = self:compl_ctx_poss()
eol = poss.eol
if (#poss == 0 and e) or #words == 0 then -- no context if (#poss == 0 and e) or #words == 0 then -- no context
if #words == 0 or (#words == 1 and not e) then -- verb? if #words == 0 or (#words == 1 and not e) then -- verb?
poss, eol = self:compl_verb(words) poss, eol = self:compl_verb(words)
@ -1333,13 +1401,16 @@ function mp:compl(str)
end end
else -- matches else -- matches
self.cache.nouns = self:nouns() self.cache.nouns = self:nouns()
poss, eol, vargs = self:compl_match(words) poss, eol = self:compl_match(words)
end end
poss.eol = eol
self:compl_ctx_push(poss) self:compl_ctx_push(poss)
end end
local _, pre = self:compl_ctx() local _, pre = self:compl_ctx()
for _, v in ipairs(poss) do for _, v in ipairs(poss) do
if v.word == '*' then vargs = true end if v.word == '*' and not v.hidden then
vargs = true
end
if self:startswith(v.word, pre) and not v.word:find("%*$") then if self:startswith(v.word, pre) and not v.word:find("%*$") then
if not dups[v.word] then if not dups[v.word] then
dups[v.word] = v dups[v.word] = v
@ -1462,7 +1533,7 @@ function mp:match(verb, w, compl)
table.insert(parsed_verb, fixed_verb) table.insert(parsed_verb, fixed_verb)
for _, d in ipairs(verb.dsc) do -- verb variants for _, d in ipairs(verb.dsc) do -- verb variants
-- local was_noun = false -- local was_noun = false
local match = { args = {}, vargs = {}, ev = d.ev, wildcards = 0, verb = parsed_verb, defaults = 0 } local match = { args = {}, vargs = {}, skip = 0, ev = d.ev, wildcards = 0, verb = parsed_verb, defaults = 0 }
local a = {} local a = {}
found = (#d.pat == 0) found = (#d.pat == 0)
for k, v in ipairs(w) do for k, v in ipairs(w) do
@ -1478,7 +1549,7 @@ function mp:match(verb, w, compl)
local vargs local vargs
for lev, v in ipairs(d.pat) do -- pattern arguments for lev, v in ipairs(d.pat) do -- pattern arguments
if v == '*' or v == '~*' then if v == '*' or v == '~*' then
vargs = true -- found vargs = v -- found
v = '*' v = '*'
end end
local noun = not not v:find("^~?{noun}") local noun = not not v:find("^~?{noun}")
@ -1496,19 +1567,20 @@ function mp:match(verb, w, compl)
need_required = true need_required = true
all_optional = false all_optional = false
end end
if pp.default then default = pp.default
if default then
word = pp.word word = pp.word
default = true
end end
local new_wildcard local new_wildcard
local k, len = word_search(a, pp.word) local k, len = word_search(a, pp.word)
if not k and mp.compare_len > 0 then if not k and mp.compare_len > 0 and not pp.synonym then
k, len = word_search(a, pp.word, starteq) k, len = word_search(a, pp.word, starteq)
new_wildcard = true new_wildcard = true
else else
new_wildcard = false new_wildcard = false
end end
if k and ((k < best or len > best_len) or if (not required or mp.strict_mode) and k ~= 1 then k = false end -- ?word is only in 1st pos
if k and ((k < best or (k == best and len > best_len)) or
(not new_wildcard and wildcard and k <= best and len >= best_len)) then (not new_wildcard and wildcard and k <= best and len >= best_len)) then
wildcard = new_wildcard wildcard = new_wildcard
best = k best = k
@ -1562,19 +1634,27 @@ function mp:match(verb, w, compl)
break break
end end
rlev = rlev + 1 rlev = rlev + 1
end
if (wildcard or match.wildcards > 0) and best > 1 then -- do not skip words if wildcard used
found = false
vargs = false vargs = false
break
end end
-- if false then -- if false then
-- a = tab_exclude(a, best, best + best_len - 1) -- a = tab_exclude(a, best, best + best_len - 1)
-- else -- else
-- if not was_noun then -- if not was_noun then
if not vargs then
match.skip = match.skip + (best - 1)
for i = 1, best - 1 do for i = 1, best - 1 do
table.insert(skip, a[i]) table.insert(skip, a[i])
end end
end
-- end -- end
a = tab_sub(a, best + best_len) a = tab_sub(a, best + best_len)
-- table.remove(a, 1) -- table.remove(a, 1)
-- end -- end
vargs = false
table.insert(match, word) table.insert(match, word)
table.insert(match.args, found) table.insert(match.args, found)
if wildcard then if wildcard then
@ -1600,9 +1680,14 @@ function mp:match(verb, w, compl)
else else
found = false found = false
if #a > 0 or #match.vargs > 0 then if #a > 0 or #match.vargs > 0 then
table.insert(hints, { word = v, lev = rlev }) while #a > 0 do
table.insert(match.vargs, a[1])
table.insert(match, a[1])
table.remove(a, 1)
end
table.insert(hints, { word = v, lev = rlev, match = match })
else else
table.insert(hints, { word = '*', lev = rlev }) table.insert(hints, { word = vargs, lev = rlev, match = match })
end end
end end
if not found then if not found then
@ -1618,10 +1703,14 @@ function mp:match(verb, w, compl)
end end
end end
if not compl and mp.errhints then if not compl and mp.errhints then
local objs = {}
for _, pp in ipairs(pat) do -- single argument for _, pp in ipairs(pat) do -- single argument
if mp.utf.len(pp.word) >= 3 then if not pp.synonym and not objs[pp.ob or 0] then
local k, _ = word_search(a, pp.word, self.lev_thresh) local k, _ = word_search(a, pp.word, self.lev_thresh)
if k then table.insert(hints, { word = pp.word, lev = rlev, fuzzy = true, match = match }) end if k then
table.insert(hints, { word = pp.word, lev = rlev, fuzzy = true, match = match })
objs[pp.ob or 1] = true
end
end end
end end
end end
@ -1634,7 +1723,11 @@ function mp:match(verb, w, compl)
match.defaults = match.defaults + 1 match.defaults = match.defaults + 1
end end
end end
table.insert(match.args, { word = false, optional = true } ) if default then
table.insert(match.args, { word = word, default = true } )
else
table.insert(match.args, { word = false, optional = true } )
end
-- table.insert(hints, { word = v, lev = rlev }) -- table.insert(hints, { word = v, lev = rlev })
found = true found = true
end end
@ -1645,29 +1738,39 @@ function mp:match(verb, w, compl)
-- end -- end
if found or all_optional then if found or all_optional then
match.extra = (#a ~= 0) match.extra = (#a ~= 0)
table.insert(match, 1, fixed_verb) -- w[verb.verb_nr]) if not match.extra or match.wildcards == 0 then
if self:skip_filter(skip) then table.insert(match, 1, fixed_verb) -- w[verb.verb_nr])
table.insert(matches, match) if self:skip_filter(skip) then
end table.insert(matches, match)
if #match.vargs == 0 and not vargs then end
match.vargs = false if #match.vargs == 0 and not vargs then
match.vargs = false
end
end end
end end
end end
for k, v in ipairs(matches) do for k, v in ipairs(matches) do
v.nr = k v.nr = k
--[[
if false then if false then
print("-----------", k) print("-----------", k)
for kk, vv in ipairs(v) do for kk, vv in ipairs(v) do
print(vv) print(vv)
end end
end end
]]--
end end
table.sort(matches, table.sort(matches,
function(a, b) function(a, b)
local na, nb = #a - a.defaults, #b - b.defaults local na, nb = #a - a.defaults, #b - b.defaults
if not a.extra and a.skip == 0 then
na = na + 100
end
if not b.extra and b.skip == 0 then
nb = nb + 100
end
if na == nb and a.wildcards == b.wildcards then if na == nb and a.wildcards == b.wildcards then
return a.nr < b.nr return a.nr < b.nr
end end
@ -1969,6 +2072,14 @@ function mp:correct(inp)
if rinp ~= '' then rinp = rinp .. ' ' end if rinp ~= '' then rinp = rinp .. ' ' end
rinp = rinp .. v rinp = rinp .. v
end end
local strip_inp = str_split(inp, inp_split)
inp = ''
for _, v in ipairs(strip_inp) do
if not mp:ignore_filter(v) then
if inp ~= '' then inp = inp .. ' ' end
inp = inp .. v
end
end
local cmprinp = rinp:gsub("["..inp_split.."]+", " ") local cmprinp = rinp:gsub("["..inp_split.."]+", " ")
if not self:eq(cmprinp, inp) then if not self:eq(cmprinp, inp) then
pn(fmt.em("("..rinp..")")) pn(fmt.em("("..rinp..")"))
@ -1989,7 +2100,7 @@ function mp:show_prompt(inp)
if std.cmd[1] == 'look' then if std.cmd[1] == 'look' then
return false return false
end end
if std.here():has 'cutscene' or std.here():has 'noprompt' or player_moved() or std.abort_cmd then if std.here():has 'cutscene' or std.here():has 'noprompt' then
return false return false
end end
if self.prompt then if self.prompt then
@ -2018,7 +2129,7 @@ function mp:parse(inp)
mp:log("> "..inp) mp:log("> "..inp)
local noprompt = not mp:show_prompt(inp) local prompt = mp:show_prompt(inp)
inp = inp:gsub("[ ]+", " "):gsub("["..inp_split.."]+", " "):gsub("[ \t]+$", "") inp = inp:gsub("[ ]+", " "):gsub("["..inp_split.."]+", " "):gsub("[ \t]+$", "")
@ -2044,7 +2155,7 @@ function mp:parse(inp)
return r return r
end end
else else
if std.cmd[1] ~= 'look' and not noprompt then if std.cmd[1] ~= 'look' and prompt ~= false then
self:correct(inp) self:correct(inp)
end end
-- here we do action -- here we do action
@ -2054,12 +2165,18 @@ function mp:parse(inp)
end end
std.world.display = function(s, state) std.world.display = function(s, state)
local l, av, pv local l, av, pv, first
if mp.text == '' and game:time() == 1 and state ~= false then if not mp.started and mp.text == '' and game:time() == 1 and state ~= false then
local r = std.call(game, 'dsc') local r = std.call(game, 'dsc')
if type(r) == 'string' then if type(r) == 'string' then
mp.text = r .. '^^' first = true
if mp._pager_mode then
mp.text = fmt.anchor() .. r .. '^^' -- .. fmt.anchor()
else
mp.text = r .. '^^'
end
end end
mp.started = true
end end
if mp.clear_on_move and game:time() ~= 1 then if mp.clear_on_move and game:time() ~= 1 then
if player_moved() then mp:clear() end if player_moved() then mp:clear() end
@ -2080,8 +2197,10 @@ std.world.display = function(s, state)
l = std.par(std.scene_delim, reaction or false, l = std.par(std.scene_delim, reaction or false,
av or false, l or false, av or false, l or false,
pv or false) or '' pv or false) or ''
mp:log(l) if l ~= '' then
if mp._pager_mode then mp:log(l)
end
if mp._pager_mode and not first then
mp.text = mp.text .. fmt.anchor() .. l .. '^^' -- .. fmt.anchor() mp.text = mp.text .. fmt.anchor() .. l .. '^^' -- .. fmt.anchor()
else else
mp.text = mp.text .. l .. '^^' -- .. fmt.anchor() mp.text = mp.text .. l .. '^^' -- .. fmt.anchor()
@ -2268,6 +2387,19 @@ function mp:shorten_input(w)
end end
end end
function mp:strip_input(w)
local i = 1
local len = #w
while i < len do
if mp:ignore_filter(w[i]) then
table.remove(w, i)
len = len - 1
else
i = i + 1
end
end
end
function mp:input(str) function mp:input(str)
-- self.cache = { tokens = {} }; -- self.cache = { tokens = {} };
local hints = {} local hints = {}
@ -2285,6 +2417,7 @@ function mp:input(str)
if not str then return false end if not str then return false end
end end
local w = str_split(str, inp_split) local w = str_split(str, inp_split)
mp:strip_input(w)
mp:shorten_input(w) mp:shorten_input(w)
self.words = w self.words = w
if #w == 0 then if #w == 0 then
@ -2452,12 +2585,13 @@ function(cmd)
std.game:__start() std.game:__start()
end end
if mp:noparser() then if mp:noparser() then
return true, false return
end end
-- mp.inp = mp:docompl(mp.inp) -- mp.inp = mp:docompl(mp.inp)
local r, v, n local r, v, n
repeat repeat
if n then if n then
std.busy(true)
std.abort_cmd = false std.abort_cmd = false
std.me():moved(false) std.me():moved(false)
std.me():need_scene(false) std.me():need_scene(false)
@ -2465,6 +2599,7 @@ function(cmd)
r, v = mp:key_enter(cmd[1] == 'look') r, v = mp:key_enter(cmd[1] == 'look')
n = true n = true
until not mp:autoplay_pending() or mp:noparser() until not mp:autoplay_pending() or mp:noparser()
std.busy(false)
mp:onedit() mp:onedit()
return r, v return r, v
end end
@ -2481,7 +2616,7 @@ function mp:autoscript(w)
end end
self.autoplay = io.open(w or 'autoscript') or false self.autoplay = io.open(w or 'autoscript') or false
if self.autoplay then if self.autoplay then
self:MetaTranscriptOn(); -- self:MetaTranscriptOn();
std.cmd = { 'autoscript' } std.cmd = { 'autoscript' }
return true return true
end end
@ -2491,6 +2626,7 @@ end
std.mod_init( std.mod_init(
function() function()
if DEBUG and mp.undo == 0 then mp.undo = 5 end if DEBUG and mp.undo == 0 then mp.undo = 5 end
mp:pager_mode(true)
_'game'.__daemons = std.list {} _'game'.__daemons = std.list {}
end) end)
@ -2516,10 +2652,10 @@ instead.mouse_filter(0)
-- speedup undo -- speedup undo
local obusy = std.busy local obusy = std.busy
local busy_count = 0 local busy_count = 0
function std.busy() function std.busy(b)
busy_count = busy_count + 1 busy_count = busy_count + 1
if (busy_count % 100) == 0 then if not b or (busy_count % 100) == 0 then
obusy() obusy(b)
end end
end end
function instead.fading() function instead.fading()
@ -2818,5 +2954,15 @@ function std.obj:has(attr)
end end
function iface:title(t) function iface:title(t)
return(iface:bold( mrd.lang.cap(t))) return(iface:bold(mrd.lang.cap(t)))
end
std.getmt("").__pow = function(a, b)
if b then
if std.is_obj(b) then
return b ^ a
end
return std.rawequal(a, b)
end
return false
end end

View File

@ -2,7 +2,9 @@
--luacheck: no self --luacheck: no self
local tostring = std.tostr local tostring = std.tostr
local table = std.table
local type = type
local string = string
--- Error handler --- Error handler
-- @param err error code -- @param err error code
function mp:err(err) function mp:err(err)
@ -25,15 +27,14 @@ function mp:err(err)
local fixed = verb.verb[verb.word_nr] local fixed = verb.verb[verb.word_nr]
if verb.verb_nr == 1 then if verb.verb_nr == 1 then
hint = true hint = true
p (mp:mesg 'UNKNOWN_VERB', " ", iface:em(self.words[verb.verb_nr]), ".") mp:message('UNKNOWN_VERB', self.words[verb.verb_nr])
p (mp:mesg 'UNKNOWN_VERB_HINT', " ", iface:em(fixed.word .. (fixed.morph or "")), "?") mp:message('UNKNOWN_VERB_HINT', fixed.word .. (fixed.morph or ""))
break break
end end
end end
end end
if not hint then if not hint then
p (mp:mesg('UNKNOWN_VERB') or "Unknown verb:", mp:message('UNKNOWN_VERB', self.words[1])
" ", iface:em(self.words[1]), ".")
end end
elseif err == "EMPTY_INPUT" then elseif err == "EMPTY_INPUT" then
p (mp:mesg('EMPTY') or "Empty input.") p (mp:mesg('EMPTY') or "Empty input.")
@ -47,10 +48,10 @@ function mp:err(err)
verb = verb .. vv .. ' ' verb = verb .. vv .. ' '
end end
verb = verb:gsub(" $", "") verb = verb:gsub(" $", "")
for _, vv in ipairs(self.hints.match) do
verb = verb .. ' '.. vv
end
for _, vv in pairs(self.hints.match.args) do for _, vv in pairs(self.hints.match.args) do
if vv.word then
verb = verb .. ' '.. vv.word
end
if vv.ob then if vv.ob then
second_noun = true second_noun = true
end end
@ -96,33 +97,38 @@ function mp:err(err)
else else
if need_noun then if need_noun then
if second_noun then if second_noun then
p (mp:mesg('INCOMPLETE_SECOND_NOUN') or mp:mesg('INCOMPLETE_NOUN'), mp:message('INCOMPLETE_SECOND_NOUN', second_noun .." " ..mp:err_noun(need_noun))
" \"", second_noun, " ", mp:err_noun(need_noun), "\"?")
elseif parsed then
p (mp:mesg('INCOMPLETE_NOUN'), " \"", parsed, "\"?")
else else
p (mp:mesg('INCOMPLETE_NOUN'), "?") mp:message('INCOMPLETE_NOUN', parsed)
end end
else else
mp:message 'INCOMPLETE' mp:message 'INCOMPLETE'
end end
end end
if not mp.errhints then if not mp.errhints or need_noun then
return return
end end
local words = {} local words = {}
local dups = {} local dups = {}
for _, v in ipairs(self.hints) do for _, v in ipairs(self.hints) do
if v:find("^~?{noun}") or v == '*' then if v:find("^~?{noun}") or v == '*' or v == '~*' then
if v:sub(1,1) == '~' then v = v:sub(2) end
v = mp:err_noun(v) v = mp:err_noun(v)
if not dups[v] and not need_noun then if not dups[v] then
table.insert(words, v) table.insert(words, v)
dups[v] = true dups[v] = true
end end
else else
local pat = self:pattern(v) local pat = self:pattern(v)
local empty = true
for _, vv in ipairs(pat) do for _, vv in ipairs(pat) do
if not vv.hidden and not dups[vv.word] then if not vv.hidden then
empty = false
break
end
end
for _, vv in ipairs(pat) do
if (empty or not vv.hidden) and not dups[vv.word] then
table.insert(words, vv.word) table.insert(words, vv.word)
dups[vv.word] = true dups[vv.word] = true
end end
@ -139,14 +145,14 @@ function mp:err(err)
end end
p (mp:mesg 'HINT_WORDS', ", ", parsed or '') p (mp:mesg 'HINT_WORDS', ", ", parsed or '')
else else
p (mp:mesg 'HINT_WORDS', " ") p (mp:mesg 'HINT_WORDS', ", ")
end end
end end
for k, v in ipairs(words) do for k, v in ipairs(words) do
if k ~= 1 then if k ~= 1 then
if k == #words then if k == #words then
pr (" ", mp.msg.HINT_OR, " ") pr (" ", mp.msg.OR, " ")
else else
pr (", ") pr (", ")
end end
@ -160,7 +166,7 @@ function mp:err(err)
pr (mp:mesg 'MULTIPLE', " ", self.multi[1]) pr (mp:mesg 'MULTIPLE', " ", self.multi[1])
for k = 2, #self.multi do for k = 2, #self.multi do
if k == #self.multi then if k == #self.multi then
pr (" ", mp.msg.HINT_AND, " ", self.multi[k]) pr (" ", mp.msg.AND, " ", self.multi[k])
else else
pr (", ", self.multi[k]) pr (", ", self.multi[k])
end end
@ -234,11 +240,11 @@ mp.door = std.class({
end; end;
}, std.obj):attr 'enterable,openable,door' }, std.obj):attr 'enterable,openable,door'
local function pnoun(noun, m, ...) function mp:pnoun(noun, msg)
local ctx = mp:save_ctx() local ctx = mp:save_ctx()
mp.first = noun mp.first = noun
mp.first_hint = noun:gram().hint mp.first_hint = noun:gram().hint
mp:message(m, ...) std.p(mp.fmt(msg)) -- first is available only here, so fmt is forced
mp:restore_ctx(ctx) mp:restore_ctx(ctx)
end end
@ -454,14 +460,25 @@ std.obj.display = function(s)
return c return c
end end
local last_gfx = false
std.player.look = function(s) std.player.look = function(s)
local scene local scene, img
local r = s:where() local r = s:where()
if s:need_scene() then if s:need_scene() then
local gfx
gfx = std.call(std.here(), 'gfx') or std.call(std.game, 'gfx')
if not gfx and instead.tiny then
gfx = stead.call(std.here(), 'pic') or stead.call(std.ref 'game', 'pic')
end
if gfx and gfx ~= last_gfx then
img = fmt.c(fmt.img(gfx))
last_gfx = gfx
end
scene = r:scene() scene = r:scene()
end end
return (std.par(std.scene_delim, scene or false, r:display() or false)) return (std.par(std.scene_delim, img or false, scene or false, r:display() or false, std.call(mp, 'footer') or false))
end; end
-- --
local function check_persist(w) local function check_persist(w)
@ -760,20 +777,64 @@ mp.compass_dir = function(_, w, dir)
return w ^ ('@'..dir) return w ^ ('@'..dir)
end end
mp.msg.MULTIDSC = function(oo, inv) mp.msg.INFODSC = function(o)
return mp:multidsc(oo, inv) return mp:infodsc(o)
end
mp.detailed_attr = {
{ 'worn' },
{ 'open', 'openable'},
-- { 'on', 'switchable'},
-- { 'light' }
}
function mp:infodsc(ob)
local info = {}
for _, v in ipairs(self.detailed_attr) do
local hit = #v > 0
for _, vv in ipairs(v) do
if ob:hasnt(vv) then
hit = false
break
end
end
if hit then
local n = 'HAS_'..string.upper(v[1])
if mp.msg[n] then
table.insert(info, mp:mesg(n, ob))
end
end
end
if #info > 0 then
pr(" (")
for k, i in ipairs(info) do
if #info > 1 and k == #info then
pr(' ', mp.msg.AND, ' ')
elseif k > 1 then
pr(", ")
end
pr(i)
end
pr(")")
end
end end
function mp:multidsc(oo, inv) function mp:multidsc(oo, inv)
local t = {} local t = {}
local dup = {} local dup = {}
local hint = type(inv) == 'string' and inv or ''
for _, v in ipairs(oo) do for _, v in ipairs(oo) do
local n local n
if not v:has'concealed' then if not v:has'concealed' then
if inv then if inv == true then
n = std.call(v, 'inv') n = std.call(v, 'inv')
end end
n = n or v:noun(1) if type(v.a_noun) == 'function' then
n = n or v:a_noun(hint, 1)
else
n = n or v:noun(hint, 1)
end
if dup[n] then if dup[n] then
dup[n] = dup[n] + 1 dup[n] = dup[n] + 1
else else
@ -793,17 +854,12 @@ function mp:multidsc(oo, inv)
end end
end end
if dup[v] > 1 then if dup[v] > 1 then
pr (vv.ob:noun(self.mrd.lang.gram_t.plural, 1), " (", dup[v], " ", mp.msg.ENUM, ")") pr (ob:noun(hint .. ','..self.mrd.lang.gram_t.plural, 1), " (", dup[v], " ", mp:mesg('ENUM', dup[v], ob), ")")
else else
pr (v) pr (v)
if ob:has'worn' then pr(mp:mesg('INFODSC', ob))
pr(mp:mesg('WORN', ob))
elseif ob:has'openable' and ob:has'open' then
pr(mp:mesg('OPEN', ob))
end
end end
end end
p "."
end end
-- Default priority in content -- Default priority in content
@ -906,38 +962,10 @@ function mp:content(w, exam)
oo = ooo oo = ooo
if #oo == 0 then if #oo == 0 then
if not inside and exam and mp.first == w and not something then if not inside and exam and mp.first == w and not something then
if w:has 'supporter' then mp:message ('Exam.NOTHING', w)
pnoun (w, 'Exam.ON')
else
pnoun (w, 'Exam.IN')
end
mp:message 'Exam.NOTHING'
end end
elseif #oo == 1 and not oo[1]:hint 'plural' then
if std.me():where() == w or std.here() == w then
mp:message('Look.HEREIS', w)
else
if w:has 'supporter' then
pnoun (w, 'Exam.ON')
else
pnoun (w, 'Exam.IN')
end
mp:message 'Exam.IS'
end
-- p(oo[1]:noun(1), ".")
mp:message('MULTIDSC', oo)
else else
if std.me():where() == w or std.here() == w then mp:message('Exam.CONTENT', w, oo)
mp:message('Look.HEREARE', w)
else
if w:has 'supporter' then
pnoun (w, 'Exam.ON')
else
pnoun (w, 'Exam.IN')
end
mp:message 'Exam.ARE'
end
mp:message('MULTIDSC', oo)
end end
-- expand? -- expand?
for _, o in ipairs(expand) do for _, o in ipairs(expand) do
@ -949,6 +977,14 @@ end
std.room:attr 'enterable,light' std.room:attr 'enterable,light'
function mp:strip(r)
if std.strip_call and type(r) == 'string' then
r = r:gsub("^[%^\n\r\t ]+", "") -- extra heading ^ and spaces
r = r:gsub("[%^\n\r\t ]+$", "") -- extra trailing ^ and spaces
end
return r
end
function mp:step() function mp:step()
local old_daemons = {} local old_daemons = {}
game.__daemons:for_each(function(o) game.__daemons:for_each(function(o)
@ -971,19 +1007,11 @@ function mp:step()
end end
end end
local s = std.game -- after reset game is recreated local s = std.game -- after reset game is recreated
local r = std.pget() local r = mp:strip(std.pget())
if std.strip_call and type(r) == 'string' then
r = r:gsub("^[%^\n\r\t ]+", "") -- extra heading ^ and spaces
r = r:gsub("[%^\n\r\t ]+$", "") -- extra trailing ^ and spaces
end
s:reaction(r or false) s:reaction(r or false)
std.pclr() std.pclr()
s:step() s:step()
r = s:display(true) r = mp:strip(s:display(true))
if std.strip_call and type(r) == 'string' then
r = r:gsub("^[%^\n\r\t ]+", "") -- extra heading ^ and spaces
r = r:gsub("[%^\n\r\t ]+$", "") -- extra trailing ^ and spaces
end
s:lastreact(s:reaction() or false) s:lastreact(s:reaction() or false)
s:lastdisp(r) s:lastdisp(r)
std.pr(r) std.pr(r)
@ -991,12 +1019,19 @@ function mp:step()
end end
function mp:post_action() function mp:post_action()
if self:noparser() or if (self.event and self.event:find("Meta", 1, true)) or self:comment() or self:noparser() then
(self.event and self.event:find("Meta", 1, true)) or if std.abort_cmd then
self:comment() then return
if not std.abort_cmd then
game:time(game:time() - 1)
end end
local s = std.game
local r = mp:strip(std.pget())
s:reaction(r or false)
std.pclr()
r = mp:strip(s:display(self:noparser()))
s:lastdisp(r)
s:lastreact(s:reaction() or false)
std.pr(r)
std.abort_cmd = true
return return
end end
if mp.undo > 0 then if mp.undo > 0 then
@ -1015,16 +1050,6 @@ function mp:post_action()
if game.player:need_scene() then if game.player:need_scene() then
-- pn(iface:nb'') -- pn(iface:nb'')
local l = game.player:look() -- objects [and scene] local l = game.player:look() -- objects [and scene]
local gfx
if std.here().gfx ~= nil then
gfx = std.call(std.here(), 'gfx')
end
if not gfx and std.game.gfx ~= nil then
gfx = std.call(std.game, 'gfx')
end
if gfx then
pn(fmt.c(fmt.img(gfx)))
end
p(l, std.scene_delim) p(l, std.scene_delim)
game.player:need_scene(false) game.player:need_scene(false)
end end
@ -1288,11 +1313,7 @@ function mp:detailed_Inv(wh, indent)
for _ = 1, indent do pr(iface:nb' ') end for _ = 1, indent do pr(iface:nb' ') end
local inv = std.call(o, 'inv') or o:noun(1) local inv = std.call(o, 'inv') or o:noun(1)
pr(inv) pr(inv)
if o:has'worn' then mp:message('INFODSC', o)
mp:message('WORN', o)
elseif o:has'openable' and o:has'open' then
mp:message('OPEN', o)
end
pn() pn()
if o:has'supporter' or o:has'container' then if o:has'supporter' or o:has'container' then
mp:detailed_Inv(o, indent + 1) mp:detailed_Inv(o, indent + 1)
@ -1322,7 +1343,8 @@ function mp:after_Inv()
mp:detailed_Inv(std.me(), 1) mp:detailed_Inv(std.me(), 1)
else else
p() p()
mp:message('MULTIDSC', oo, true) mp:multidsc(oo, true)
p "."
end end
end end
@ -1393,7 +1415,7 @@ function mp:mesg(m, ...)
for _, n in ipairs(t) do for _, n in ipairs(t) do
m = m[n] m = m[n]
if not m then if not m then
std.err("Wrong message id", 2) std.err("Wrong message id: "..tostring(n), 2)
end end
end end
if type(m) ~= 'function' then if type(m) ~= 'function' then
@ -1634,6 +1656,14 @@ local function cont_taken(ob, taken)
end end
end end
--- Check if object is part of parent
-- @param w what
function mp:partof(w)
return w:where() and not w:where():type'room' and
not w:where():has'container' and
not w:where():has'supporter'
end
function mp:TakeAll(wh) function mp:TakeAll(wh)
local empty = true local empty = true
wh = wh or std.me():where() wh = wh or std.me():where()
@ -1643,7 +1673,8 @@ function mp:TakeAll(wh)
for _, o in ipairs(oo) do for _, o in ipairs(oo) do
if o:hasnt 'static' and o:hasnt'scenery' and o:hasnt 'concealed' if o:hasnt 'static' and o:hasnt'scenery' and o:hasnt 'concealed'
and not mp:animate(o) and not mp:animate(o)
and not cont_taken(o, taken) then and not cont_taken(o, taken)
and not mp:partof(o) then
empty = false empty = false
mp:message('TAKING_ALL', o) mp:message('TAKING_ALL', o)
mp:subaction('Take', o) mp:subaction('Take', o)
@ -1692,9 +1723,7 @@ function mp:Take(w, wh)
mp:message 'Take.SCENERY' mp:message 'Take.SCENERY'
return return
end end
if w:where() and not w:where():type'room' and if mp:partof(w) then
not w:where():has'container' and
not w:where():has'supporter' then
if w:has'worn' and mp:animate(w:where()) then if w:has'worn' and mp:animate(w:where()) then
mp:message 'Take.WORN' mp:message 'Take.WORN'
else else
@ -1724,6 +1753,10 @@ function mp:Remove(w, wh)
mp:message 'Remove.WHERE' mp:message 'Remove.WHERE'
return return
end end
if wh == std.me() then
mp:xaction('Disrobe', w, wh)
return
end
mp:xaction('Take', w, wh) mp:xaction('Take', w, wh)
end end
@ -2783,6 +2816,12 @@ function mp:MetaHelp()
pn(mp:mesg 'HELP') pn(mp:mesg 'HELP')
end end
function mp:MetaScore()
mp:message'TITLE_TURNS'
pn()
mp:message'TITLE_SCORE'
end
function mp:MetaTranscript() function mp:MetaTranscript()
if self.logfile then if self.logfile then
p("Log file: ", self.logfile) p("Log file: ", self.logfile)
@ -2812,6 +2851,21 @@ function mp:MetaTranscriptOn()
self.lognum = self.lognum + 1 self.lognum = self.lognum + 1
end end
end end
function mp:MetaVersion()
p(mp.version)
end
function mp:MetaVerbs()
local verbs = {}
for _, v in ipairs(mp:verbs()) do
local vv = v.verb[1]
if vv and not vv.hidden then
local verb = vv.word .. (vv.morph or "")
table.insert(verbs, verb)
end
end
table.sort(verbs)
for _, v in ipairs(verbs) do p(v) end
end
mp.msg.MetaRestart = {} mp.msg.MetaRestart = {}
@ -3091,13 +3145,14 @@ instead.get_title = function(_)
local col = instead.theme_var('win.col.fg') local col = instead.theme_var('win.col.fg')
local score = '' local score = ''
if mp.score then if mp.score then
score = fmt.tab('70%', 'center')..fmt.nb(mp:mesg('TITLE_SCORE') .. tostring(mp.score).."/"..tostring(mp.maxscore)) score = fmt.tab('70%', 'center')..fmt.nb(mp:mesg('TITLE_SCORE'))
end end
local moves = fmt.tab('100%', 'right')..fmt.nb(mp:mesg('TITLE_TURNS') .. tostring(game:time() - 1)) local moves = fmt.tab('100%', 'right')..fmt.nb(mp:mesg('TITLE_TURNS'))
return iface:left((title.. score .. moves).."\n".. iface:img(string.format("box:%dx1,%s", w, col))) return iface:left((title.. score .. moves).."\n".. iface:img(string.format("box:%dx1,%s", w, col)))
end end
--luacheck: globals content --luacheck: globals content
function content(...) function content(w, ...)
return mp:content(...) w = std.object(w)
return mp:content(w, ...)
end end