mirror of
https://gitlab.com/Oreolek/salet.git
synced 2024-07-07 09:14:24 +03:00
166 lines
5.4 KiB
CoffeeScript
166 lines
5.4 KiB
CoffeeScript
# I confess that this world model heavily borrows from INSTEAD engine. - A.Y.
|
|
|
|
undum = require('./undum.js')
|
|
RaconteurSituation = require('./situation.coffee')
|
|
obj = require('./obj.coffee')
|
|
markdown = require('./markdown.coffee')
|
|
|
|
way_to = (content, ref) ->
|
|
return "<a href='#{ref}' class='way' id='waylink-#{ref}'>#{content}</a>"
|
|
|
|
# jQuery was confused by this point where's the context so I did it vanilla-way
|
|
print = (content) ->
|
|
if typeof content == "function"
|
|
content = content()
|
|
block = document.getElementById("current-situation")
|
|
if block
|
|
block.innerHTML = block.innerHTML + markdown(content)
|
|
else #the game is not initialized yet. This is dangerous and will not augment any links.
|
|
block = document.getElementById("content")
|
|
block.innerHTML = markdown(content)
|
|
|
|
Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1
|
|
|
|
addClass = (element, className) ->
|
|
if (element.classList)
|
|
element.classList.add(className)
|
|
else
|
|
element.className += ' ' + className
|
|
|
|
# Function to return the current room.
|
|
# Works because our `enter()` function sets the `data-situation` attribute.
|
|
here = () ->
|
|
return undum.game.situations[document.getElementById("current-situation").getAttribute("data-situation")]
|
|
|
|
update_ways = (ways) ->
|
|
content = ""
|
|
distances = []
|
|
if ways
|
|
for way in ways
|
|
if undum.game.situations[way]?
|
|
title = undum.game.situations[way].name
|
|
content += way_to(title, way)
|
|
distances.push({
|
|
key: way
|
|
distance: undum.game.situations[way].distance
|
|
})
|
|
document.getElementById("ways").innerHTML = content
|
|
min = Infinity
|
|
min_key = []
|
|
for node in distances
|
|
if node.distance < min
|
|
min = node.distance
|
|
min_key = [node.key]
|
|
if node.distance == min
|
|
min_key.push(node.key)
|
|
if min < Infinity
|
|
for node in min_key
|
|
addClass(document.getElementById("waylink-#{node}"), "destination")
|
|
|
|
class SaletRoom extends RaconteurSituation
|
|
constructor: (spec) ->
|
|
RaconteurSituation.call(this, spec)
|
|
if spec.objects?
|
|
@objects = spec.objects
|
|
if spec.exit?
|
|
@exit = spec.exit
|
|
return this
|
|
objects: []
|
|
distance: Infinity # distance to the destination
|
|
|
|
###
|
|
I call SaletRoom.exit every time the player exits to another room.
|
|
###
|
|
exit: (character, system, to) ->
|
|
###
|
|
Undum calls Situation.enter every time a situation is entered, and
|
|
passes it three arguments; The character object, the system object,
|
|
and a string referencing the previous situation, or null if there is
|
|
none (ie, for the starting situation).
|
|
|
|
My version of `enter` splits the location description from the effects.
|
|
Also if f == this.name (we're in the same location) the `before` and `after` callbacks are ignored.
|
|
###
|
|
enter: (character, system, f) ->
|
|
#system.clearContent()
|
|
if f != @name and f?
|
|
@visited++
|
|
undum.game.situations[f].exit(character, system, @name)
|
|
|
|
if not @extendSection
|
|
classes = if @classes then ' ' + @classes.join(' ') else ''
|
|
situation = document.getElementById('current-situation')
|
|
if situation?
|
|
situation.setAttribute('id', undefined)
|
|
system.write("<section id='current-situation' data-situation='#{@name}' class='situation-#{@name}#{classes}'>")
|
|
|
|
if f != @name and @before?
|
|
print(@before.fcall(this, character, system, f))
|
|
|
|
if @look
|
|
@look character, system, f
|
|
|
|
if f != @name and @after?
|
|
print(@after.fcall(this, character, system, f))
|
|
|
|
if not @extendSection
|
|
system.write("</section>")
|
|
|
|
if @choices
|
|
system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices))
|
|
|
|
look: (character, system, f) ->
|
|
update_ways(@ways)
|
|
|
|
# Print the room description
|
|
if @content
|
|
system.write(markdown(@content.fcall(this, character, system, f)))
|
|
|
|
if @objects? then for thing in @objects
|
|
system.write thing.look()
|
|
|
|
###
|
|
Object action. A function or a string which comes when you click on the object link.
|
|
You could interpret this as an EXAMINE verb or USE one, it's your call.
|
|
###
|
|
act: (character, system, action) ->
|
|
# default Raconteur action
|
|
if (action.match(/^_(writer|replacer|inserter)_.+$/))
|
|
return RaconteurSituation.prototype.act.call(this, character, system, f)
|
|
|
|
if (link = action.match(/^_act_(.+)$/)) #object action
|
|
for thing in @objects
|
|
if thing.name == link[1]
|
|
# We check the "take" function. If it exists, the player can take this object.
|
|
# If not, we check the "act" function.
|
|
if thing.take
|
|
@objects.remove(thing)
|
|
character.sandbox.inventory.push thing
|
|
@enter(character, system, @name)
|
|
return print(thing.take.fcall(thing, character, system))
|
|
if thing.act
|
|
return print(thing.act.fcall(thing, character, system))
|
|
# the loop is done but no return came - match not found
|
|
console.error("Could not find #{link[1]} in current room.")
|
|
|
|
# Marks every room in the game with distance to this room
|
|
destination: () ->
|
|
@distance = 0
|
|
|
|
candidates = [this]
|
|
while candidates.length > 0
|
|
current_room = candidates.shift()
|
|
if current_room.ways
|
|
for node in current_room.ways
|
|
if node.distance == Infinity
|
|
node.distance = current_room.distance + 1
|
|
candidates.push(node)
|
|
|
|
room = (name, spec) ->
|
|
if spec
|
|
spec.name = name
|
|
retval = new SaletRoom(spec)
|
|
return retval.register()
|
|
|
|
module.exports = room
|