mirror of
https://gitlab.com/Oreolek/salet.git
synced 2024-06-01 07:48:20 +03:00
Salet is now passed as an argument.
Also, no frequency filters on choice list.
This commit is contained in:
parent
566c8b27a2
commit
5e7f4087aa
|
@ -3,7 +3,15 @@ room = require("../../lib/room.coffee")
|
|||
obj = require('../../lib/obj.coffee')
|
||||
dialogue = require('../../lib/dialogue.coffee')
|
||||
oneOf = require('../../lib/oneOf.coffee')
|
||||
require('../../lib/salet.coffee')
|
||||
Salet = require('../../lib/salet.coffee')
|
||||
|
||||
salet = new Salet
|
||||
salet.view.init(salet)
|
||||
salet.game_id = "your-game-id-here"
|
||||
salet.game_version = "1.0"
|
||||
$(document).ready(() ->
|
||||
salet.beginGame()
|
||||
)
|
||||
|
||||
###
|
||||
Element helpers. There is no real need to build monsters like a().id("hello")
|
||||
|
@ -25,7 +33,7 @@ cyclelink = (content) ->
|
|||
|
||||
# The first room of the game.
|
||||
# For accessibility reasons the text is provided in HTML, not here.
|
||||
room "start",
|
||||
room "start", salet,
|
||||
dsc: """
|
||||
""",
|
||||
choices: "#start"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# This is where you initialize your game.
|
||||
|
||||
$(document).ready(() ->
|
||||
salet.game_id = "your-game-id-here"
|
||||
salet.game_version = "1.0"
|
||||
salet.beginGame()
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
room "world",
|
||||
room "world", salet,
|
||||
tags: ["start"],
|
||||
optionText: "Enter the world",
|
||||
ways: ["plaza"]
|
||||
|
@ -13,7 +13,7 @@ room "world",
|
|||
dsc: "A steep narrow {{well}} proceeds upward."
|
||||
act: "There is only one passage out. See the „Other rooms“ block popped up? Click it."
|
||||
|
||||
room "plaza",
|
||||
room "plaza", salet,
|
||||
title: (from) ->
|
||||
if from == "world"
|
||||
return "Upwards"
|
||||
|
@ -44,7 +44,7 @@ room "plaza",
|
|||
dsc: "There are {{people shouting}} nearby."
|
||||
act: 'Just some weirdos shouting "Viva la Cthulhu!". Typical.'
|
||||
|
||||
room "shop",
|
||||
room "shop", salet,
|
||||
title: "The Shop"
|
||||
#pic: "http://loremflickr.com/640/300/room,shop"
|
||||
ways: ["plaza", "shop-inside", "lair"]
|
||||
|
@ -55,7 +55,7 @@ room "shop",
|
|||
You are standing in front of a picturesque sign. It's cold here.
|
||||
"""
|
||||
|
||||
room "lair",
|
||||
room "lair", salet,
|
||||
title: "The Lair"
|
||||
before: "Finding The Lair is easy. Leaving it is impossible. Your game ends here."
|
||||
dsc: """
|
||||
|
@ -69,11 +69,11 @@ room "lair",
|
|||
here().drop(@name)
|
||||
return "You eat the bugg mass. Delicious and raw. Perhaps it's a good lair to live in."
|
||||
|
||||
dialogue "Yes", "merchant", "merchant", """
|
||||
dialogue "Yes", salet, "merchant", "merchant", """
|
||||
Yes.
|
||||
"""
|
||||
|
||||
room "shop-inside",
|
||||
room "shop-inside", salet,
|
||||
ways: ["shop"]
|
||||
tags: ["merchant"]
|
||||
optionText: "End the conversation"
|
||||
|
@ -100,7 +100,7 @@ lamp = obj "lamp",
|
|||
lamp.put("shop-inside")
|
||||
###
|
||||
|
||||
room "merchdialogue",
|
||||
room "merchdialogue", salet,
|
||||
choices: "#merchant",
|
||||
dsc: """
|
||||
Nice day, isn't it?
|
||||
|
|
|
@ -15,8 +15,8 @@ Usage:
|
|||
Point out a thing in her purse (mildly)
|
||||
""", "character.sandbox.mild = true"
|
||||
###
|
||||
dialogue = (title, startTag, endTag, text, effect) ->
|
||||
retval = room(randomid(), {
|
||||
dialogue = (title, salet, startTag, endTag, text, effect) ->
|
||||
retval = room(randomid(), salet, {
|
||||
optionText: title
|
||||
dsc: text
|
||||
clear: false # backlog is useful in dialogues
|
||||
|
|
|
@ -4,7 +4,7 @@ Implies that you don't mix up your tabs and spaces.
|
|||
Copyright 2015 Bruno Dias
|
||||
###
|
||||
normaliseTabs = (text) ->
|
||||
unless text?
|
||||
unless text? and typeof(text) == "string"
|
||||
return ""
|
||||
lines = text.split('\n');
|
||||
indents = lines
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
# I confess that this world model heavily borrows from INSTEAD engine. - A.Y.
|
||||
|
||||
require('./salet.coffee')
|
||||
obj = require('./obj.coffee')
|
||||
markdown = require('./markdown.coffee')
|
||||
cycle = require('./cycle.coffee')
|
||||
Random = require('./random.js')
|
||||
languages = require('./localize.coffee')
|
||||
require('./salet.coffee')
|
||||
|
||||
# Assertion
|
||||
assert = console.assert
|
||||
assert = (msg, assertion) -> console.assert assertion, msg
|
||||
|
||||
way_to = (content, ref) ->
|
||||
return "<a href='#{ref}' class='way' id='waylink-#{ref}'>#{content}</a>"
|
||||
|
@ -21,43 +19,6 @@ addClass = (element, className) ->
|
|||
else
|
||||
element.className += ' ' + className
|
||||
|
||||
update_ways = (ways, name) ->
|
||||
content = ""
|
||||
distances = []
|
||||
if ways then for way in ways
|
||||
if salet.rooms[way]?
|
||||
title = salet.rooms[way].title.fcall(this, name)
|
||||
content += way_to(title, way)
|
||||
distances.push({
|
||||
key: way
|
||||
distance: salet.rooms[way].distance
|
||||
})
|
||||
else
|
||||
document.querySelector(".ways h2").style.display = "none"
|
||||
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")
|
||||
|
||||
picture_tag = (picture) ->
|
||||
extension = picture.substr((~-picture.lastIndexOf(".") >>> 0) + 2)
|
||||
if (extension == "webm")
|
||||
return """
|
||||
<video src="#{picture}" controls>
|
||||
Your browser does not support the video tag for some reason.
|
||||
You won't be able to view this video in this browser.
|
||||
</video>
|
||||
"""
|
||||
return "<img class='img-responsive' src='#{picture}' alt='Room illustration'>"
|
||||
|
||||
class SaletRoom
|
||||
constructor: (spec) ->
|
||||
for index, value of spec
|
||||
|
@ -73,7 +34,6 @@ class SaletRoom
|
|||
canView: true
|
||||
canChoose: true
|
||||
priority: 1
|
||||
frequency: 1
|
||||
displayOrder: 1
|
||||
tags: []
|
||||
choices: ""
|
||||
|
@ -84,14 +44,14 @@ class SaletRoom
|
|||
distance: Infinity # distance to the destination
|
||||
clear: true # clear the screen on entering the room?
|
||||
|
||||
entering: (character, system, from) =>
|
||||
entering: (system, from) =>
|
||||
|
||||
###
|
||||
I call SaletRoom.exit every time the player exits to another room.
|
||||
Unlike @after this gets called after the section is closed.
|
||||
It's a styling difference.
|
||||
###
|
||||
exit: (character, system, to) =>
|
||||
exit: (system, to) =>
|
||||
return true
|
||||
|
||||
###
|
||||
|
@ -102,7 +62,7 @@ class SaletRoom
|
|||
The upstream Undum version does not allow you to redefine @enter function easily but allows custom @exit one.
|
||||
It was renamed as @entering to achieve API consistency.
|
||||
###
|
||||
enter: (character, system, from) =>
|
||||
enter: (system, from) =>
|
||||
return true
|
||||
|
||||
###
|
||||
|
@ -148,25 +108,25 @@ class SaletRoom
|
|||
if not @extendSection
|
||||
room_content += "</section>"
|
||||
|
||||
system.write(room_content)
|
||||
system.view.write(room_content)
|
||||
|
||||
if @choices
|
||||
system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices))
|
||||
system.view.writeChoices(system, system.getSituationIdChoices(@choices, @maxChoices))
|
||||
|
||||
###
|
||||
An internal function to get the room's description and the descriptions of
|
||||
every object in this room.
|
||||
###
|
||||
look: (character, system, f) =>
|
||||
update_ways(@ways, @name)
|
||||
look: (system, f) =>
|
||||
system.view.updateWays(system, @ways, @name)
|
||||
retval = ""
|
||||
|
||||
if @pic
|
||||
retval += '<div class="pic">'+picture_tag(@pic.fcall(this, character, system, f))+'</div>'
|
||||
retval += '<div class="pic">'+system.view.pictureTag(@pic.fcall(this, system, f))+'</div>'
|
||||
|
||||
# Print the room description
|
||||
if @dsc
|
||||
retval += markdown(@dsc.fcall(this, character, system, f))
|
||||
retval += markdown(@dsc.fcall(this, system, f))
|
||||
|
||||
for name, thing of @objects
|
||||
retval += thing.look()
|
||||
|
@ -189,7 +149,7 @@ class SaletRoom
|
|||
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) =>
|
||||
act: (system, action) =>
|
||||
if (link = action.match(/^_(act|cycle)_(.+)$/)) #object action
|
||||
for name, thing of @objects
|
||||
if name == link[2]
|
||||
|
@ -215,15 +175,15 @@ class SaletRoom
|
|||
|
||||
responses = {
|
||||
writer: (ref) ->
|
||||
content = that.writers[ref].fcall(that, character, system, action)
|
||||
content = that.writers[ref].fcall(that, system, action)
|
||||
output = markdown(content)
|
||||
system.writeInto(output, '#current-room')
|
||||
replacer: (ref) ->
|
||||
content = that.writers[ref].fcall(that, character, system, action)
|
||||
content = that.writers[ref].fcall(that, system, action)
|
||||
output = "<span>"+content+"</span>" # <p> tags are usually bad for replacers
|
||||
system.replaceWith(output, '#'+ref)
|
||||
inserter: (ref) ->
|
||||
content = that.writers[ref].fcall(that, character, system, action)
|
||||
content = that.writers[ref].fcall(that, system, action)
|
||||
output = markdown(content)
|
||||
system.writeInto(output, '#'+ref)
|
||||
}
|
||||
|
@ -236,7 +196,7 @@ class SaletRoom
|
|||
throw new Error("Tried to call undefined writer: #{action}");
|
||||
responses[responder](ref);
|
||||
else if (@actions.hasOwnProperty(action))
|
||||
@actions[action].call(this, character, system, action);
|
||||
@actions[action].call(this, system, action);
|
||||
else
|
||||
throw new Error("Tried to call undefined action: #{action}");
|
||||
|
||||
|
@ -253,7 +213,7 @@ class SaletRoom
|
|||
node.distance = current_room.distance + 1
|
||||
candidates.push(node)
|
||||
|
||||
register: () =>
|
||||
register: (salet) =>
|
||||
if not @name?
|
||||
console.error("Situation has no name")
|
||||
return this
|
||||
|
@ -264,17 +224,9 @@ class SaletRoom
|
|||
cyclewriter: (character) ->
|
||||
cycle(this.cycle, this.name, character)
|
||||
|
||||
room = (name, spec) ->
|
||||
room = (name, salet, spec) ->
|
||||
spec ?= {}
|
||||
spec.name = name
|
||||
retval = new SaletRoom(spec)
|
||||
$(document).ready(() ->
|
||||
if salet
|
||||
retval.register()
|
||||
else
|
||||
sleep(1000)
|
||||
retval.register()
|
||||
)
|
||||
return retval
|
||||
return new SaletRoom(spec).register(salet)
|
||||
|
||||
module.exports = room
|
||||
|
|
166
lib/salet.coffee
166
lib/salet.coffee
|
@ -1,6 +1,7 @@
|
|||
markdown = require('./markdown.coffee')
|
||||
SaletView = require('./view.coffee')
|
||||
Random = require('./random.js')
|
||||
languages = require('./localize.coffee')
|
||||
|
||||
###
|
||||
fcall() (by analogy with fmap) is added to the prototypes of both String and
|
||||
|
@ -9,6 +10,8 @@ when called on a String, it only returns the string itself, discarding any input
|
|||
###
|
||||
|
||||
Function.prototype.fcall = Function.prototype.call;
|
||||
Boolean.prototype.fcall = () ->
|
||||
return this
|
||||
String.prototype.fcall = () ->
|
||||
return this
|
||||
|
||||
|
@ -33,31 +36,6 @@ parseFn = (str) ->
|
|||
# Salet's default is a general URL-safe expression.
|
||||
linkRe = /^([0-9A-Za-z_-]+|\.)(\/([0-9A-Za-z_-]+))?$/
|
||||
|
||||
# Returns HTML from the given content with the non-raw links wired up.
|
||||
augmentLinks = (content) ->
|
||||
output = $(content)
|
||||
|
||||
# Wire up the links for regular <a> tags.
|
||||
output.find("a").each((index, element) ->
|
||||
a = $(element)
|
||||
href = a.attr('href')
|
||||
if (!a.hasClass("raw")|| href.match(/[?&]raw[=&]?/))
|
||||
if (href.match(linkRe))
|
||||
a.click((event) ->
|
||||
event.preventDefault()
|
||||
|
||||
# If we're a once-click, remove all matching links.
|
||||
if (a.hasClass("once") || href.match(/[?&]once[=&]?/))
|
||||
@view.clearLinks(href)
|
||||
|
||||
processClick(href)
|
||||
return false
|
||||
)
|
||||
else
|
||||
a.addClass("raw")
|
||||
)
|
||||
return output
|
||||
|
||||
###
|
||||
This is the control structure, it has minimal amount of data and
|
||||
this data is volatile anyway (as in, it won't get saved).
|
||||
|
@ -149,17 +127,6 @@ class Salet
|
|||
situations that force the player to go to one destination if
|
||||
the player is out of money, for example.
|
||||
|
||||
If a minChoices value is given, then the function will attempt
|
||||
to return at least that many results. If not enough results are
|
||||
available at the highest priority, then lower priorities will
|
||||
be considered in turn, until enough situations are found. In
|
||||
the example above, if we had a minChoices of three, then all
|
||||
three situations would be returned, even though they have
|
||||
different priorities. If you need to return all valid
|
||||
situations, regardless of their priorities, set minChoices to a
|
||||
large number, such as `Number.MAX_VALUE`, and leave maxChoices
|
||||
undefined.
|
||||
|
||||
If a maxChoices value is given, then the function will not
|
||||
return any more than the given number of results. If there are
|
||||
more than this number of results possible, then the highest
|
||||
|
@ -181,90 +148,65 @@ class Salet
|
|||
Before this function returns its result, it sorts the
|
||||
situations in increasing order of their displayOrder values.
|
||||
###
|
||||
getSituationIdChoices: (listOfOrOneIdsOrTags, minChoices, maxChoices) ->
|
||||
getSituationIdChoices: (listOfOrOneIdsOrTags, maxChoices) ->
|
||||
datum = null
|
||||
i = 0
|
||||
|
||||
# First check if we have a single string for the id or tag.
|
||||
if ($.type(listOfOrOneIdsOrTags) == 'string')
|
||||
if (typeof(listOfOrOneIdsOrTags) == 'string')
|
||||
listOfOrOneIdsOrTags = [listOfOrOneIdsOrTags]
|
||||
|
||||
# First we build a list of all candidate ids.
|
||||
allIds = {}
|
||||
allIds = []
|
||||
for tagOrId in listOfOrOneIdsOrTags
|
||||
if (tagOrId.substr(0, 1) == '#')
|
||||
ids = getSituationIdsWithTag(tagOrId.substr(1))
|
||||
ids = @getRoomsTagged(tagOrId.substr(1))
|
||||
for id in ids
|
||||
allIds[id] = true
|
||||
else
|
||||
allIds[tagOrId] = true
|
||||
allIds.push(id)
|
||||
else #it's an id, not a tag
|
||||
allIds.push(tagOrId)
|
||||
|
||||
#Filter out anything that can't be viewed right now.
|
||||
currentSituation = @getCurrentRoom()
|
||||
viewableSituationData = []
|
||||
for situationId in allIds
|
||||
situation = @rooms[situationId]
|
||||
assert(situation, "unknown_situation".l({id:situationId}))
|
||||
currentRoom = @getCurrentRoom()
|
||||
viewableRoomData = []
|
||||
for roomId in allIds
|
||||
room = @rooms[roomId]
|
||||
assert(room, "unknown_situation".l({id:roomId}))
|
||||
|
||||
if (situation.canView(character, salet, currentSituation))
|
||||
#While we're here, get the selection data.
|
||||
viewableSituationDatum = situation.choiceData(character, salet, currentSituation)
|
||||
viewableSituationDatum.id = situationId
|
||||
viewableSituationData.push(viewableSituationDatum)
|
||||
if (room.canView.fcall(this, currentRoom))
|
||||
viewableRoomData.push({
|
||||
priority: room.priority
|
||||
id: roomId
|
||||
displayOrder: room.displayOrder
|
||||
})
|
||||
|
||||
# Then we sort in descending priority order.
|
||||
viewableSituationData.sort((a, b) ->
|
||||
viewableRoomData.sort((a, b) ->
|
||||
return b.priority - a.priority
|
||||
)
|
||||
|
||||
committed = []
|
||||
candidatesAtLastPriority = []
|
||||
lastPriority
|
||||
# In descending priority order.
|
||||
for datum in viewableSituationData
|
||||
if (datum.priority != lastPriority)
|
||||
if (lastPriority != undefined)
|
||||
# We've dropped a priority group, see if we have enough
|
||||
# situations so far, and stop if we do.
|
||||
if (minChoices == undefined || i >= minChoices)
|
||||
break
|
||||
# Continue to acccumulate more options.
|
||||
committed.push.apply(committed, candidatesAtLastPriority);
|
||||
candidatesAtLastPriority = [];
|
||||
lastPriority = datum.priority;
|
||||
candidatesAtLastPriority.push(datum);
|
||||
|
||||
# So the values in committed we're committed to, because without
|
||||
# them we wouldn't hit our minimum. But those in
|
||||
# candidatesAtLastPriority might take us over our maximum, so
|
||||
# figure out how many we should choose.
|
||||
totalChoices = committed.length + candidatesAtLastPriority.length
|
||||
if (maxChoices == undefined || maxChoices >= totalChoices)
|
||||
# We can use all the choices.
|
||||
committed.push.apply(committed, candidatesAtLastPriority)
|
||||
else if (maxChoices >= committed.length)
|
||||
# We can only use the commited ones.
|
||||
# NO-OP
|
||||
else
|
||||
# We have to sample the candidates, using their relative frequency.
|
||||
candidatesToInclude = maxChoices - committed.length;
|
||||
for datum in candidatesAtLastPriority
|
||||
datum._frequencyValue = this.rnd.random() / datum.frequency;
|
||||
candidatesToInclude.sort((a, b) ->
|
||||
return a.frequencyValue - b.frequencyValue;
|
||||
)
|
||||
chosen = candidatesToInclude.slice(0, candidatesToInclude)
|
||||
committed.push.apply(committed, chosen)
|
||||
# if we need to filter out the results
|
||||
if (maxChoices? && viewableRoomData.length > maxChoices)
|
||||
viewableRoomData = viewableRoomData[-maxChoices..]
|
||||
|
||||
for candidateRoom in viewableRoomData
|
||||
committed.push({
|
||||
id: candidateRoom.id
|
||||
displayOrder: candidateRoom.displayOrder
|
||||
})
|
||||
|
||||
# Now sort in ascending display order.
|
||||
committed.sort((a, b) ->
|
||||
return a.displayOrder - b.displayOrder
|
||||
return a.displayOrder - b.displayOrder
|
||||
)
|
||||
|
||||
# And return as a list of ids only.
|
||||
result = []
|
||||
for i in committed
|
||||
result.push(i.id)
|
||||
|
||||
return result
|
||||
|
||||
# This is the data on the player's progress that gets saved.
|
||||
|
@ -313,7 +255,7 @@ class Salet
|
|||
@linkStack = []
|
||||
|
||||
# Handle each link in turn.
|
||||
processOneLink(code);
|
||||
@processOneLink(code);
|
||||
while (@linkStack.length > 0)
|
||||
code = linkStack.shift()
|
||||
processOneLink(code)
|
||||
|
@ -322,10 +264,10 @@ class Salet
|
|||
@linkStack = null;
|
||||
|
||||
# Scroll to the top of the new content.
|
||||
@endOutputTransaction()
|
||||
@view.endOutputTransaction()
|
||||
|
||||
# We're able to save, if we weren't already.
|
||||
@enableSaving()
|
||||
@view.enableSaving()
|
||||
|
||||
###
|
||||
This gets called to actually do the work of processing a code.
|
||||
|
@ -341,9 +283,8 @@ class Salet
|
|||
action = match[3]
|
||||
|
||||
# Change the situation
|
||||
if situation != '.'
|
||||
if situation != current
|
||||
@doTransitionTo(situation)
|
||||
if situation != '.' and situation != @current_room
|
||||
@doTransitionTo(situation)
|
||||
else
|
||||
# We should have an action if we have no situation change.
|
||||
assert(action, "link_no_action".l())
|
||||
|
@ -370,9 +311,9 @@ class Salet
|
|||
# This gets called when the user clicks a link to carry out an action.
|
||||
processClick: (code) ->
|
||||
now = (new Date()).getTime() * 0.001
|
||||
@time = now - startTime
|
||||
@time = now - @startTime
|
||||
@progress.sequence.push({link:code, when:@time})
|
||||
processLink(code)
|
||||
@processLink(code)
|
||||
|
||||
# Transitions between situations.
|
||||
doTransitionTo: (newRoomId) ->
|
||||
|
@ -385,11 +326,11 @@ class Salet
|
|||
# We might not have an old situation if this is the start of the game.
|
||||
if (oldRoom and @exit)
|
||||
@exit(oldRoomId, newRoomId)
|
||||
|
||||
|
||||
@current = newRoomId
|
||||
|
||||
# Remove links and transient sections.
|
||||
@view.remove_transient(@interactive)
|
||||
@view.removeTransient(@interactive)
|
||||
|
||||
# Notify the incoming situation.
|
||||
if (@enter)
|
||||
|
@ -407,19 +348,19 @@ class Salet
|
|||
game state across save/erase cycles, meaning that character.sandbox
|
||||
no longer has to be the end-all be-all repository of game state.
|
||||
###
|
||||
erase_save: (force = false) =>
|
||||
eraseSave: (force = false) =>
|
||||
save_id = @getSaveId() # save slot
|
||||
if (localStorage.getItem(saveId) and (force or confirm("erase_message".l())))
|
||||
localStorage.removeItem(saveId)
|
||||
window.location.reload()
|
||||
|
||||
# Find and return a list of ids for all situations with the given tag.
|
||||
getSituationIdsWithTag: (tag) =>
|
||||
getRoomsTagged: (tag) =>
|
||||
result = []
|
||||
for situationId, situation of @situations
|
||||
for i in situation.tags
|
||||
for id, room of @rooms
|
||||
for i in room.tags
|
||||
if (i == tag)
|
||||
result.push(situationId)
|
||||
result.push(id)
|
||||
break
|
||||
return result
|
||||
|
||||
|
@ -477,7 +418,7 @@ class Salet
|
|||
@view.disableSaving()
|
||||
@view.enableErasing()
|
||||
catch err
|
||||
@erase_save(true)
|
||||
@eraseSave(true)
|
||||
else
|
||||
@progress.seed = new Date().toString()
|
||||
|
||||
|
@ -485,8 +426,6 @@ class Salet
|
|||
@rnd = new Random(@progress.seed)
|
||||
@progress.sequence = [{link:@start, when:0}]
|
||||
|
||||
@view.clearContent()
|
||||
|
||||
# Start the game
|
||||
@startTime = new Date().getTime() * 0.001
|
||||
if (@init)
|
||||
|
@ -515,11 +454,4 @@ class Salet
|
|||
return Boolean place.visited
|
||||
return 0
|
||||
|
||||
# Set up the game when everything is loaded.
|
||||
$(document).ready(() ->
|
||||
salet = new Salet
|
||||
salet.view.init()
|
||||
if (salet.view.hasLocalStorage())
|
||||
$("#erase").click(salet.erase_save) # is Salet defined here?
|
||||
$("#save").click(salet.saveGame)
|
||||
)
|
||||
module.exports = Salet
|
||||
|
|
102
lib/view.coffee
102
lib/view.coffee
|
@ -1,3 +1,4 @@
|
|||
markdown = require('./markdown.coffee')
|
||||
###
|
||||
Salet interface configuration.
|
||||
In a typical MVC structure, this is the View.
|
||||
|
@ -10,8 +11,13 @@ game as he wants to. The save and erase buttons are not necessary buttons,
|
|||
but they could be something else entirely. (That's why IDs are hardcoded.)
|
||||
###
|
||||
|
||||
assert = (msg, assertion) -> console.assert assertion, msg
|
||||
|
||||
way_to = (content, ref) ->
|
||||
return "<a href='#{ref}' class='way'>#{content}</a>"
|
||||
|
||||
class SaletView
|
||||
init: () ->
|
||||
init: (salet) ->
|
||||
$("#content, #ways").on("click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
salet.processClick($(this).attr("href"))
|
||||
|
@ -23,6 +29,9 @@ class SaletView
|
|||
$("#load").on("click", "a", (event) ->
|
||||
window.location.reload()
|
||||
)
|
||||
if (@hasLocalStorage())
|
||||
$("#erase").click(salet.erase_save) # is Salet defined here?
|
||||
$("#save").click(salet.saveGame)
|
||||
|
||||
disableSaving: () ->
|
||||
$("#save").addClass('disabled')
|
||||
|
@ -67,11 +76,15 @@ class SaletView
|
|||
return
|
||||
if typeof content == "function"
|
||||
content = content()
|
||||
if content instanceof jQuery
|
||||
content = content[0].outerHTML
|
||||
block = document.getElementById("current-room")
|
||||
if block
|
||||
block.innerHTML = block.innerHTML + markdown(content)
|
||||
else
|
||||
console.error("No current situation found.")
|
||||
# most likely this is the starting room
|
||||
block = document.getElementById("content")
|
||||
block.innerHTML = content
|
||||
|
||||
###
|
||||
Turns any links that target the given href into plain
|
||||
|
@ -98,25 +111,25 @@ class SaletView
|
|||
manually, ot else use the `getSituationIdChoices` method to
|
||||
return an ordered list of valid viewable situation ids.
|
||||
###
|
||||
writeChoices: (listOfIds) ->
|
||||
if (listOfIds.length == 0)
|
||||
writeChoices: (salet, listOfIds) ->
|
||||
if (not listOfIds? or listOfIds.length == 0)
|
||||
return
|
||||
|
||||
currentSituation = getCurrentSituation();
|
||||
currentRoom = salet.getCurrentRoom();
|
||||
$options = $("<ul>").addClass("options");
|
||||
for situationId in listOfIds
|
||||
situation = game.situations[situationId]
|
||||
assert(situation, "unknown_situation".l({id:situationId}))
|
||||
if (situation == currentSituation)
|
||||
for roomId in listOfIds
|
||||
room = salet.rooms[roomId]
|
||||
assert(room, "unknown_situation".l({id:roomId}))
|
||||
if (room == currentRoom)
|
||||
continue
|
||||
|
||||
optionText = situation.optionText.fcall(this, character, this, currentSituation)
|
||||
optionText = room.optionText.fcall(salet, currentRoom)
|
||||
if (!optionText)
|
||||
optionText = "choice".l({number:i+1})
|
||||
$option = $("<li>")
|
||||
$a = $("<span>")
|
||||
if (situation.canChoose(character, this, currentSituation))
|
||||
$a = $("<a>").attr({href: situationId})
|
||||
if (room.canChoose.fcall(this, salet, currentRoom))
|
||||
$a = $("<a>").attr({href: roomId})
|
||||
$a.html(optionText)
|
||||
$option.html($a)
|
||||
$options.append($option)
|
||||
|
@ -129,8 +142,9 @@ class SaletView
|
|||
# Removes links and transient sections.
|
||||
# Arguments:
|
||||
# interactive - if we're working in interactive mode (or we're loading a save)
|
||||
remove_transient: (interactive = false) ->
|
||||
for a in $('#content a')
|
||||
removeTransient: (interactive = false) ->
|
||||
for a in $('#content').find('a')
|
||||
a = $(a)
|
||||
if (a.hasClass('sticky') || a.attr("href").match(/[?&]sticky[=&]?/))
|
||||
return;
|
||||
a.replaceWith($("<span>").addClass("ex_link").html(a.html()))
|
||||
|
@ -193,4 +207,64 @@ class SaletView
|
|||
hasStorage = false
|
||||
return hasStorage
|
||||
|
||||
updateWays: (salet, ways, name) ->
|
||||
content = ""
|
||||
distances = []
|
||||
if ways then for way in ways
|
||||
if salet.rooms[way]?
|
||||
title = salet.rooms[way].title.fcall(this, name)
|
||||
content += way_to(title, way)
|
||||
distances.push({
|
||||
key: way
|
||||
distance: salet.rooms[way].distance
|
||||
})
|
||||
else
|
||||
document.querySelector(".ways h2").style.display = "none"
|
||||
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")
|
||||
|
||||
pictureTag = (picture) ->
|
||||
extension = picture.substr((~-picture.lastIndexOf(".") >>> 0) + 2)
|
||||
if (extension == "webm")
|
||||
return """
|
||||
<video src="#{picture}" controls>
|
||||
Your browser does not support the video tag for some reason.
|
||||
You won't be able to view this video in this browser.
|
||||
</video>
|
||||
"""
|
||||
return "<img class='img-responsive' src='#{picture}' alt='Room illustration'>"
|
||||
|
||||
# Returns HTML from the given content with the non-raw links wired up.
|
||||
augmentLinks: (content) ->
|
||||
output = $(content)
|
||||
|
||||
# Wire up the links for regular <a> tags.
|
||||
for index, element in output.find("a")
|
||||
a = $(element)
|
||||
href = a.attr('href')
|
||||
if (!a.hasClass("raw")|| href.match(/[?&]raw[=&]?/))
|
||||
if (href.match(linkRe))
|
||||
a.click((event) ->
|
||||
event.preventDefault()
|
||||
|
||||
# If we're a once-click, remove all matching links.
|
||||
if (a.hasClass("once") || href.match(/[?&]once[=&]?/))
|
||||
@view.clearLinks(href)
|
||||
|
||||
processClick(href)
|
||||
return false
|
||||
)
|
||||
else
|
||||
a.addClass("raw")
|
||||
return output
|
||||
module.exports = SaletView
|
||||
|
|
Loading…
Reference in a new issue