Начало игры

This commit is contained in:
Alexander Yakovlev 2017-02-08 22:29:35 +07:00
parent d9dc8ea5ef
commit 2515a5ff91
8 changed files with 166 additions and 317 deletions

View file

@ -43,9 +43,6 @@ gulp.task('sass', () ->
gulp.task('concatCoffee', () ->
gulp.src([
## additional functions
'./game/dialogue.coffee',
'./game/phrase.coffee',
## the actual game
'./game/begin.coffee',
'./game/story.coffee',

View file

@ -1,5 +1,5 @@
salet.game_id = "your-game-id-here"
salet.game_version = "1.6"
salet.game_id = "2156049f-ca32-4460-a363-feb1caaa2a39"
salet.game_version = "1.0"
$(document).ready(() ->
window.addEventListener('popstate', (event) ->
salet.goBack()
@ -28,46 +28,9 @@ $(document).ready(() ->
salet.beginGame()
)
###
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>"
# The first room of the game.
# For accessibility reasons the text is provided in HTML, not here.
room "start",
enter: () ->
salet.character.bought_lamp = false
dsc: """
""",
choices: "#start"
# This is a special inventory room.
# The inventory button is a regular link to this room.
# You may alter these as much as you like or scrap it along with the button.
room "inventory",
canSave: false # saving the game here is forbidden. Aautosaving too.
enter: () ->
$("#inventory").hide()
exit: () ->
$("#inventory").show()
dsc: () ->
if salet.character.inventory.length == 0
text = "You are carrying nothing."
else
text = "You are carrying:\n\n"
for thing in salet.character.inventory
text += "* #{salet.character.listinv(thing.name)}\n"
return text+"\n\n"+"""
<div class="center"><a href="./exit"><button class="btn btn-lg btn-outline-primary">Go back</button></a></div>
"""
actions:
exit: () ->
return salet.goBack()

View file

@ -1,23 +0,0 @@
###
A dialogue shortcut.
Usage:
dialogue "Point out a thing in her purse (mildly)", "start", "mild", """
Point out a thing in her purse (mildly)
""", "character.mild = true"
###
dialogue = (title, startTag, endTag, text, effect) ->
retval = room("dialogue_"+Object.keys(salet.rooms).length, {
optionText: title
dsc: text
clear: false # backlog is useful in dialogues
choices: "#"+endTag
})
if typeof(startTag) == "string"
retval.tags = [startTag]
else if typeof(startTag) == "object"
retval.tags = startTag
if effect?
retval.before = (character, system) ->
eval(effect)
return retval

View file

@ -1,26 +0,0 @@
###
A phrase shortcut.
Usage:
phrase "Point out a thing in her purse (mildly)", "start", """
Point out a thing in her purse (mildly)
""", "character.sandbox.mild = true"
@param title phrase Phrase (question)
@param salet Salet core
@param string tag tag marking viewing condition
@param string text Response
@param string effect an optional parameter, eval'd code
###
phrase = (title, tag, text, effect) ->
retval = room("phrase_"+salet.rooms.length, {
optionText: title
dsc: text
clear: false # backlog is useful in dialogues
choices: "#"+tag
tags: [tag]
})
if effect?
retval.before = (character, system) ->
eval(effect)
return retval

View file

@ -1,126 +1,63 @@
room "world",
tags: ["start"],
optionText: "Enter the world",
ways: ["plaza"]
dsc: """
### Rhinestone Room
croom = (name, spec) ->
spec.clear = false
if spec.onexit
spec.exit = () ->
salet.view.write('<span class="shake">'+@onexit+'</span>')
return true
return room(name, spec)
You're in a large room carved inside a giant milky rock mountain.
The floor and walls are littered with signs and signatures of the previous visitors.
croom "start",
dsc: """
Вы спокойно работаете в уютном кабинете #{way_to('без мебели и окон.', 'line2')}
"""
units: [
unit "well",
dsc: "A steep narrow {{well}} proceeds upward."
act: "There is only one passage out. See the „Other rooms“ block popped up? Click it."
]
onexit: "шурх-шурх-шурх"
room "plaza",
title: (from) ->
if from == "world"
return "Upwards"
else
return "Town plaza"
cycle: ["quirky", "distinct", "kooky", "crazy", "quaint"]
ways: ["shop"]
before: (from) ->
if from == 'world'
"""
You climb up the well and come out to a central plaza of a #{salet.view.cycleLink("quaint")} little town.
A plaque nearby says it's the town of *Innsmouth,* wherever that is.
"""
else
"You quickly find the central plaza."
units: [
unit "policeman",
dsc: "There is a policeman nearby. You could ask him {{for directions.}}"
act: () ->
if salet.character.has_mark?
return "You already talked to him, no need to bug the man twice."
salet.character.has_mark ?= true
salet.getRoom("lair").destination()
"""
Here, let me mark it on your map.
"""
unit "people",
dsc: "There are {{people shouting}} nearby."
act: 'Just some weirdos shouting "Viva la Cthulhu!". Typical.'
]
room "shop",
title: "The Shop"
#pic: "http://loremflickr.com/640/300/room,shop"
ways: ["plaza", "shop-inside", "lair"]
croom "line2",
dsc: """
Being the only shop in town, this trendy establishment did not need a name.
It's an open question why it had one, especially because its name was "Hung Crossing".
Вы перемалываете листья платкодрева.
Это возможно только голыми руками, #{way_to('в полной темноте.', 'line3')}
"""
onexit: "шурх-шурх-шурх"
You are standing in front of a picturesque sign. It's cold here.
croom "line3",
dsc: """
Плотные кожистые лист падают с потолка.
Вы слышите, как он застывает в углу комнаты.
Длинный влажный хобот касается ноги.
Кромюл ищет перемолотые листья и всасывает их.
Вам не нужно вставать.
#{way_to('Продолжайте перемалывать.', 'dream1')}
"""
onexit: "шурх-шурх-шурх"
croom "dream1",
dsc: """
Из воды чёрного от грязи моря выходит разбойник.
На нём намного больше волос, чем одежды.
Его лодка разбилась на камнях, его друзья в лучшем мире.
"""
options: ["dream2a", "dream2b"]
croom "dream2a",
optionText: "Он направляется по берегу."
dsc: """
"""
room "lair",
title: "The Lair"
before: "Finding The Lair is easy. Leaving it is impossible. Your game ends here."
croom "dream2b",
optionText: "Он направляется вглубь земли."
dsc: """
The Lair of Yog-Sothoth is a very *n'gai* cave, full of *buggs-shoggogs* and *n'ghaa ng'aa*.
"""
ways: ["shop"]
units: [
unit "bugg",
dsc: "You see a particularly beautiful slimy {{bugg.}}"
takeable: false
display: "bugg"
act: () =>
salet.rooms[salet.current].drop("bugg")
return "You eat the bugg mass. Delicious and raw. Perhaps it's a good lair to live in."
]
phrase "Yes", "merchant", """
Yes.
"""
dialogue "No", "merchant", "merchant", """
No.
"""
room "sell-lamp",
ways: ["shop"]
tags: ["merchant"]
choices: ["#merchant"]
optionText: "May I buy this lamp?"
title: "Talking with merchant"
canView: () ->
return salet.character.has("lamp") and salet.character.bought_lamp == false
enter: () ->
salet.character.bought_lamp = true
dsc: """
"That'll be 30 pieces of your time."
You quickly pay the price and take the lamp as a rightful owner.
"""
room "shop-inside",
ways: ["shop"]
tags: ["merchant"]
optionText: "End the conversation"
title: "Inside the Shop"
croom "line4",
dsc: """
The insides are painted pastel white, honouring The Great Milk Spill of 1985.
"""
units: [
unit "merchant",
dsc: "A {{merchant}} eyes you warily."
takeable: false
act: () =>
salet.processClick("merchdialogue")
return ""
]
Пары платкодрева вызывают галлюцинации.
Но кромюлы не волнуются.
Что с того, что человек видит сны наяву?
lamp = unit "lamp",
display: "lamp"
takeable: true
lamp.put("shop-inside")
А ещё эти пары ядовиты.
Кромюлы не волнуются.
# The dialogue entry point has to be a room, in order to have an ID to go to.
room "merchdialogue",
choices: "#merchant",
dsc: """
Nice day, isn't it?
А вам зачем беспокоиться?
"""

View file

@ -1,64 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Salet showcase</title>
<title>Чистильщик</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href='https://fonts.googleapis.com/css?family=PT+Sans:400,400italic|PT+Sans+Caption' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="page">
<div class="fixed container">
<div id="tools_wrapper" class="row">
<div class='ways'>
<small class="text-muted" id="ways_hint">Click these links to explore other rooms</small>
<ul class="nav nav-pills" id="ways">
</ul>
</div>
<div class='ways'></div>
<div class="buttons">
<a href="inventory"><button id="inventory" class="btn btn-outline-primary">Inventory</button></a>
<button id="night" class="btn btn-outline-primary">Night mode</button>
<button id="erase" class="btn btn-outline-danger">Restart</button>
<button id="night" class="btn btn-outline-primary">Ночной режим</button>
<button id="erase" class="btn btn-outline-danger">Заново</button>
</div>
</div> <!-- End of div.tools_wrapper -->
</div>
<div class="container">
<div class="row">
<div id="title" class="title">
<div class="label">
<h1>Salet</h1>
<h2>A general cybertext IF engine</h2>
<noscript>
<p class="noscript_message">This game requires Javascript.</p>
</noscript>
</div>
</div>
</div>
<div id="content_wrapper" class="row">
<div id="intro" class="content">
<section>
<!-- For the accessibility reasons, you really should begin your game here.
Write a short intro the player will read while the Javascript is loading and executing.
It has to be HTML but that shouldn't be a BIG problem if you're reading this.
On the other hand this means the first intro passages will be non-interactive. -->
<p><em>Salet</em> is an offspring of Undum and Raconteur.</p>
<p>The project is still a work-in-progress. This "game" will show you some of the new features.</p>
<p>It's supposed to be relatively painless for the author without sacrificing the reader's experience.
For example, the game still loads while you're reading this.</p>
<p><em>Salet</em> is <em>not</em> a library or a framework you can slap something on.
It's more a game you can hack into your game. (Like Undum, yeah.)</p>
<p>There are no either cool editor or clear instructions.
You'll have to copy the source code and edit the CoffeeScript files in the <code>game</code> folder.
Then <em>compile</em> them.</p>
<p>This is neither a technical documentation nor UI tutorial, this is a <em>game.</em>
You're supposed to note all the cool and curious features yourself.
If you want some technical info, there are the <a href="http://git.oreolek.ru/oreolek/salet">source code</a> and a <a href="https://salet.su">wiki.</a></p>
<p>I'm just here to point out that you are playing this in the web browser, online.
And you don't need to learn <em>The Complete Javascript, Volumes I-III</em> to write in this style.</p>
<p>So let me show you... The World.</p>
<noscript>You need to turn on Javascript to play this game.</noscript>
</section>
</div>
@ -66,17 +29,6 @@
</div>
<a name="end_of_content"></a>
</div>
<div id="legal" class="row">
<div id="footleft">
<p>
If you want to know how this game was made, check out <a
href="https://git.oreolek.ru/oreolek/salet" target="_blank">the
source code.</a>
</p>
</div>
<div id="footright">
</div>
</div>
</div>
</div> <!-- End of div.page -->

View file

@ -1,46 +1,7 @@
@import "variables";
@import "../node_modules/bootstrap/scss/bootstrap.scss";
@import "shake";
// The title block
.title {
margin-top: 3.5em;
@extend .col-xs-12;
.label {
margin-top: 1.5em;
margin-bottom: 1em;
@extend .col-md-8;
@extend .offset-md-2;
@extend .col-xs-12;
text-align: center;
}
.subtitle {
font-size: smaller;
color: #aaa;
}
h2 {
font-size: 1.5rem;
}
.warnings {
font-size: small;
font-style: italic;
p {
margin-bottom: 1em;
}
}
.noscript_message {
left: 0;
right: 0;
bottom: 0;
position: absolute;
font-size: 0.9em;
font-style: italic;
text-align: center;
color: #943;
}
}
#choices {
@extend .col-xs-12;
}
.fixed {
position: fixed;
left: 0;
@ -103,27 +64,6 @@
text-align: center;
}
}
#legal {
margin-top: 1em;
color: darken($body-color, 10%);
font-size: smaller;
#footleft {
@extend .col-md-5;
@extend .offset-md-2;
@extend .col-xs-12;
}
#footright {
text-align: right;
@extend .col-md-2;
@extend .offset-md-2;
@extend .col-xs-12;
}
}
.way {
color: $waycolor;
margin-right: 1em;
}
.cycle {
color: darkgreen;
border-bottom: darkgreen dashed 1px;
@ -156,3 +96,9 @@ hr {
border: none;
border-color: transparent;
}
.shake {
color: grey;
font-size: smaller;
margin-left: 3em;
@include do-shake('shake-crazy', 5, 5, 20, 150ms, .1, $opacity: false, $q: 5);
}

103
sass/shake.scss Normal file
View file

@ -0,0 +1,103 @@
// Variables
$prefix: 'shake' !default;
$trigger: 'shake-trigger' !default;
// Placeholders
%shake {
display: inline-block;
transform-origin: center center;
}
%paused { animation-play-state: paused; }
%running { animation-play-state: running; }
@function apply-random($input) {
@return if($input != 0, random($input) - $input/2, 0);
}
/// Do The Shake
/// @param {String} $name ['shake'] - Name for the keyframes animation
/// @param {Number} $h [5px] - Max number for random to assign in x axis
/// @param {Number} $v [5px] - Max number for random to assign in y axis
/// @param {Number} $r [3deg] - Max number for random rotation
/// @param {Number} $dur [100ms] - animation-duration; valid time value
/// @param {Number} $precision [.1] - Precision of the keyframes animation (i.e 2 > 2%, 4%, 6%...)
/// @param {Boolean} $opacity [false] - To apply random animation also in the opacity property
/// @param {String} $q [infinite] - animation-iteration-count; valid value
/// @param {String} $t [ease-in-out] - animation-timing-function; valid value
/// @param {Number} $delay [null] - animation-delay; valid time value
/// @param {Number} $chunk [100%] - Part of 100% where apply the animation
@mixin do-shake(
$name: 'shake',
$h: 5px,
$v: 5px,
$r: 3deg,
$dur: 100ms,
$precision: .02,
$opacity: false,
$q: infinite,
$t: ease-in-out,
$delay: null,
$chunk: 100%
) {
$rotate: 0;
$move-x: 0;
$move-y: 0;
$h: if(unitless($h) and $h != 0, $h * 1px, $h);
$v: if(unitless($v) and $v != 0, $v * 1px, $v);
$r: if(unitless($r) and $r != 0, $r * 1deg, $r);
// Keyframes
@at-root {
@keyframes #{$name} {
$interval: if($precision > 0 and $precision < 1, 100 * $precision, 10);
$step: $interval * 1%;
@while $step < $chunk {
$rotate: apply-random($r);
$move-x: apply-random($h);
$move-y: apply-random($v);
#{$step} {
transform: translate($move-x, $move-y) rotate($rotate);
@if $opacity { opacity: random(100) / 100; }
}
$step: $step + $interval;
}
#{ if($chunk < 100%, (0%, $chunk, 100%), (0%, 100%)) } {
transform: translate(0, 0) rotate(0);
}
}
}
@extend %shake;
&:hover,
.#{$trigger}:hover &,
&.#{$prefix}-freeze,
&.#{$prefix}-constant {
@if $delay { animation-delay: $delay; }
animation-name: #{$name};
animation-duration: $dur;
animation-timing-function: $t;
animation-iteration-count: $q;
}
&:hover,
.#{$trigger}:hover & { @extend %running; }
}
.#{$prefix}-freeze,
.#{$prefix}-constant.#{$prefix}-constant--hover:hover,
.#{$trigger}:hover .#{$prefix}-constant.#{$prefix}-constant--hover {
@extend %paused;
}
.#{$prefix}-freeze:hover,
.#{$trigger}:hover .#{$prefix}-freeze { @extend %running; }