275 lines
7.6 KiB
CoffeeScript
275 lines
7.6 KiB
CoffeeScript
###
|
|
# This is the file with all logic code *inside* Salet.
|
|
# This is not story code but something around the story and inside the game.
|
|
###
|
|
|
|
###
|
|
Element helpers. There is no real need to build monsters like a().id("hello")
|
|
because you won't use them as is. It does not make sense in context, the
|
|
author has Markdown and all utilities to *forget* about the markup.
|
|
###
|
|
way_to = (content, ref) ->
|
|
return "<a href='#{ref}' class='way'>#{content}</a>"
|
|
textlink = (content, ref) ->
|
|
return "<a href='./_writer_#{ref}' class='once'>#{content}</a>"
|
|
actlink = (content, ref) ->
|
|
return "<a href='./#{ref}' class='once'>#{content}</a>"
|
|
choice = (text, url) ->
|
|
retval = '<a'
|
|
if url?
|
|
retval += " href='#{url}'"
|
|
retval += "><div><div class='title'>#{text}</div></div></a>"
|
|
return retval
|
|
|
|
sysroom = (name, options) ->
|
|
options.canSave = false
|
|
options.exit = () ->
|
|
if document.querySelector('#current-room')
|
|
salet.view.clearContent('#current-room')
|
|
$(".action").show()
|
|
options.dsc ?= () ->
|
|
return @text.fcall()+"\n\n"+"""
|
|
<div class="center"><a href="./exit" tabindex=999><button class="btn btn-lg btn-outline-primary">#{"back".l()}</button></a></div>
|
|
"""
|
|
options.text ?= () -> name.l()
|
|
options.actions = {
|
|
exit: () ->
|
|
return salet.goBack()
|
|
}
|
|
return croom(name, options)
|
|
|
|
croom = (name, spec) ->
|
|
spec.clear ?= true
|
|
spec.optionColor ?= ""
|
|
spec.optionText ?= () ->
|
|
retval = """
|
|
<div class="#{spec.optionColor}">
|
|
<div class="title">#{spec.title.fcall()}</div>
|
|
"""
|
|
if (spec.subtitle?)
|
|
retval += """
|
|
<div class="subtitle">#{spec.subtitle.fcall()}</div>
|
|
"""
|
|
retval += '</div>'
|
|
spec.dsc ?= () -> name.l()
|
|
return room(name, spec)
|
|
|
|
$(document).on("room_enter", (event, data) ->
|
|
# Piwik analytics: room stats
|
|
if salet.interactive and _paq?
|
|
_paq.push(['trackPageView', data.to])
|
|
)
|
|
|
|
sysroom "inventory",
|
|
text: () ->
|
|
if salet.character.inventory.length == 0
|
|
text = "inventory_empty".l()
|
|
else
|
|
text = "inventory_contains".l()+"\n\n"
|
|
for thing in salet.character.inventory
|
|
text += "* #{salet.character.listinv(thing.name)}\n"
|
|
|
|
$(document).on("room_language_after_choices", () ->
|
|
$(".options").addClass("narrowchoice")
|
|
)
|
|
sysroom "language",
|
|
dsc: ""
|
|
choices: "#language"
|
|
|
|
sysroom "ru",
|
|
title: "Русский",
|
|
tags: ["language"]
|
|
dsc: ""
|
|
enter: () ->
|
|
i18n.lang = "ru"
|
|
salet.goTo('maze')
|
|
|
|
sysroom "en",
|
|
title: "English",
|
|
tags: ["language"]
|
|
canChoose: false
|
|
dsc: ""
|
|
enter: () ->
|
|
i18n.lang = "en"
|
|
salet.goTo('maze')
|
|
|
|
sysroom "settings",
|
|
tags: ["menu"]
|
|
title: () -> "settings_title".l()
|
|
text: () ->
|
|
nightclass = ""
|
|
if window.night
|
|
nightclass = "active"
|
|
return "credits".l() + """\n
|
|
<ul class="options">
|
|
<li id="night" tabindex=1 class="#{nightclass}">#{choice("night".l())}</li>
|
|
<li tabindex=2 onclick="TogetherJS(this); return false;">
|
|
#{choice("multiplayer".l())}
|
|
</li>
|
|
</ul>
|
|
"""
|
|
|
|
sysroom "inventory",
|
|
text: () ->
|
|
if salet.character.inventory.length == 0
|
|
text = "inventory_empty".l()
|
|
else
|
|
text = "inventory_contains".l()+"\n\n"
|
|
for thing in salet.character.inventory
|
|
text += "* #{salet.character.listinv(thing.name)}\n"
|
|
|
|
sysroom "map",
|
|
text: () ->
|
|
out = "<table class='map'>"
|
|
for y in [0..(salet.character.maze.height-1)]
|
|
out += "<tr>"
|
|
for x in [0..(salet.character.maze.width-1)]
|
|
cell = salet.character.maze.at(x,y)
|
|
className = cell.getTag("type")
|
|
if x == salet.character.x and y == salet.character.y
|
|
className += " current"
|
|
out += "<td class='"+className+"'>"
|
|
out += cell.log()
|
|
out += "</td>"
|
|
out += "</tr>\n"
|
|
out += "</table>"
|
|
return out
|
|
|
|
Array.prototype.remove = (args...) ->
|
|
output = []
|
|
for arg in args
|
|
index = @indexOf arg
|
|
output.push @splice(index, 1) if index isnt -1
|
|
output = output[0] if args.length is 1
|
|
output
|
|
|
|
Array.prototype.shuffle = () ->
|
|
i = this.length
|
|
while --i > 0
|
|
j = ~~(salet.rnd.randf() * (i + 1))
|
|
t = this[j]
|
|
this[j] = this[i]
|
|
this[i] = t
|
|
this
|
|
|
|
# Volume from 0 to 1
|
|
get_volume = () ->
|
|
return ($('#slider').slider('value') - 1) / 100
|
|
|
|
window.set_volume = (value) ->
|
|
document.getElementById("bgsound").volume = (value / 100)
|
|
volume = $('.voldisplay')
|
|
if value <= 5
|
|
volume.css('background-position', '0 0')
|
|
else if value <= 25
|
|
volume.css('background-position', '0 -25px')
|
|
else if value <= 75
|
|
volume.css('background-position', '0 -50px')
|
|
else
|
|
volume.css('background-position', '0 -75px')
|
|
|
|
update_paths = (character) ->
|
|
x = character.x
|
|
y = character.y
|
|
maze = character.maze
|
|
if maze.isEast(x,y)
|
|
text = "east".l()
|
|
if not character.has("compass")
|
|
eastCell = character.getCell(x+1,y)
|
|
type = eastCell.getTag("type")
|
|
text = "#{type}".l()
|
|
$("#east").html("<a href='east'>#{text}</a>")
|
|
else
|
|
$("#east").html("")
|
|
if maze.isWest(x,y)
|
|
text = "west".l()
|
|
if not character.has("compass")
|
|
westCell = character.getCell(x-1,y)
|
|
type = westCell.getTag("type")
|
|
text = "#{type}".l()
|
|
$("#west").html("<a href='west'>#{text}</a>")
|
|
else
|
|
$("#west").html("")
|
|
if maze.isNorth(x,y)
|
|
text = "north".l()
|
|
if not character.has("compass")
|
|
northCell = character.getCell(x,y-1)
|
|
type = northCell.getTag("type")
|
|
text = "#{type}".l()
|
|
$("#north").html("<a href='north'>#{text}</a>")
|
|
else
|
|
$("#north").html("")
|
|
if maze.isSouth(x,y)
|
|
text = "south".l()
|
|
if not character.has("compass")
|
|
southCell = character.getCell(x,y+1)
|
|
type = southCell.getTag("type")
|
|
text = "#{type}".l()
|
|
$("#south").html("<a href='south'>#{text}</a>")
|
|
else
|
|
$("#south").html("")
|
|
return
|
|
|
|
typeIsArray = ( value ) ->
|
|
value and
|
|
typeof value is 'object' and
|
|
value instanceof Array and
|
|
typeof value.length is 'number' and
|
|
typeof value.splice is 'function' and
|
|
not ( value.propertyIsEnumerable 'length' )
|
|
|
|
###
|
|
# Improv's mismatchFilter with tag options.
|
|
# Ensures that the group and model don't have any mismatched tags.
|
|
# Returns null if found a mismatch between model tag and the group.
|
|
# Every match counts as +1 to the score offset.
|
|
# Returns 0 if unsure.
|
|
#
|
|
# Unlike the default mismatchFilter, it allows tags like this:
|
|
#
|
|
# ['to', ['thickforest', 'rareforest', 'deepforest']]
|
|
#
|
|
###
|
|
tagMismatchFilter = (group, model) ->
|
|
score = 0
|
|
if group.tags.length == 0
|
|
return 0
|
|
for groupTag in group.tags
|
|
for modelTag in model.tags
|
|
if modelTag[0] != groupTag[0]
|
|
continue
|
|
modelTag = modelTag[1]
|
|
groupTag = groupTag[1]
|
|
if typeIsArray(groupTag)
|
|
if groupTag.indexOf(modelTag) != -1
|
|
score = score + 1
|
|
else
|
|
return null
|
|
else
|
|
if modelTag != groupTag
|
|
return null
|
|
return score
|
|
|
|
setAdjacent = (model) ->
|
|
cells = {}
|
|
cells["north"] = salet.character.maze.getNorth(salet.character.x, salet.character.y)
|
|
cells["west"] = salet.character.maze.getWest(salet.character.x, salet.character.y)
|
|
cells["east"] = salet.character.maze.getEast(salet.character.x, salet.character.y)
|
|
cells["south"] = salet.character.maze.getSouth(salet.character.x, salet.character.y)
|
|
adjacent = []
|
|
for direction, cell of cells
|
|
if cell == false
|
|
continue
|
|
type = cell.getTag('type')
|
|
if adjacent.indexOf(type) != -1 # уже есть сосед этого типа
|
|
continue
|
|
adjacent.push(type)
|
|
model.tags.push(["adjacent", adjacent])
|
|
return model
|
|
|
|
class ImprovModel
|
|
constructor: () ->
|
|
@tags = []
|
|
# склонение существительных в родительный падеж
|
|
genitive: (word) ->
|