mapgen/game/model.coffee
2018-07-14 15:23:27 +07:00

280 lines
7.9 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["#{type}_direction"] = "#{direction}".l()
model["#{type}_direction_instrumental"] = "#{direction}_instrumental".l()
model["#{type}_direction_genitive"] = "#{direction}_genitive".l()
model["#{type}_direction_vocative"] = "#{direction}_vocative".l()
model["#{type}_direction_adverb"] = "#{direction}_adverb".l()
model.tags.push(["adjacent", adjacent])
return model
class ImprovModel
constructor: () ->
@tags = []
# склонение существительных в родительный падеж
genitive: (word) ->