CodePen

HTML

            
              mixin radio(id, checked)
  input(type="radio" id="ra_#{id}" name="ra" checked=checked)
  
mixin checkbox(id, checked)
  input(type="checkbox" id="ch_#{id}" checked=checked)
  
mixin door(id, vertical)
  label.door(for="ra_#{id}" class=vertical?'vertical':'horizontal')
  
mixin room(name)
  .room(class=name data-room=name)
    if block
      block
    

+checkbox('play')
+checkbox('bed')
+checkbox('stove')
+checkbox('sink')
+checkbox('table')
+checkbox('fireplace')

+radio(1,true)
+radio(2)
+radio(3)
+radio(4)

button.control.up(accesskey="w")
button.control.left(accesskey="a")
button.control.down(accesskey="s")
button.control.right(accesskey="d")
label.info(for="ch_play") i



+room("bedroom")
  +door(1)
  .carpet
  .carpet._2
  .carpet._3
  .carpet._4
  .deko2
  .deko2._2
  .plant
  .plant._2
  label.bed(for="ch_bed") 
+room("livingroom")
  +door(2)
  +door(3, true)
  .carpet
  .plant
  .plant._2
  .basket
  label.fireplace(for="ch_fireplace")
  .deko1
  .deko2
  .girl(data-speak="Hi Edgar, how are you doing?" tabindex="0")
+room("kitchen")
  +door(4, true)
  label.stove(for="ch_stove")   
  label.sink(for="ch_sink")
  label.table(for="ch_table")
    .plant

  
.character(
  data-fireplace="The fire is nice and warm"
  data-sink="I don't want to do the dishes now"
  data-stove="I'm not hungy right now"
  data-table="This is a table"
  data-bed="Zzzzzzzzzz"
  )


.explain
  h1.title Edgars Lair
    span a pure CSS RPG by 
      a(href="https://twitter.com/GregorAdams" target="blank") @GregorAdams
  .modal
    .key ctrl
    |  +
    .key alt
    |  + 
    .key W
    |  or
    .key A
    |  or
    .key S
    |  or
    .key D
    |  then
    .key space
    |  or click buttons to walk
    br
    | Click on wall-gaps to change the room.
    br
    | Click on objects to interact.
    br
    label.button(for="ch_play") play
  audio(controls loop autoplay)
    source(src="http://pixelass.com/rpg_sound_3.mp3")    
    source(src="http://pixelass.com/rpg_sound_3.ogg")
            
          
!

CSS

            
              /// Stroke font-character
/// @param  {Integer} $stroke - Stroke width
/// @param  {Color}   $color  - Stroke color
/// @return {List}            - text-shadow list
@function stroke($stroke, $color) {
  $shadow: ();
  $from: $stroke*-1;
  @for $i from $from through $stroke {
   @for $j from $from through $stroke {
      $shadow: append($shadow, $i*1px $j*1px 0 $color, comma);
    }
  }
  @return $shadow;
}
/// Stroke font-character
/// @param  {Integer} $stroke - Stroke width
/// @param  {Color}   $color  - Stroke color
/// @return {Style}           - text-shadow
@mixin stroke($stroke, $color) {
  text-shadow: stroke($stroke, $color);
}
/// Image resource
/// @param  {String} $name - full name with extension
/// @return {Url}          - url(...) with full path
@function image($name) {
  @return url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/28359/#{$name}');
}
@import url(http://fonts.googleapis.com/css?family=VT323);
$room-size: 320px;
$character-sprite-size: 128px;
$character-height: $character-sprite-size/4;
$character-width: $character-sprite-size/4;
$room-offset-x: ($room-size - $character-width)/2;
$room-offset-y: ($room-size - $character-height)/2;
$wall-width: 8;


body {
  width: 100vw;
  height: 100vh;
  margin: 0;
  z-index: 0;
  overflow: hidden;
  background: black;
  user-select: none;
  &:before {
    content: '';
    position: absolute;
    z-index: 2;
    top: 0;
    left: 0;
    height: 170px;
    width: 170px;
    background-clip: padding-box;
    background-image: image('modal_bg.png');
    border-image: image("modal_bd.png") 8 repeat;
  }
}

*, *:before, *:after  {
  box-sizing: border-box;
  color: transparent;
  &:focus {
    outline: 0;
  }
}

.control {
  position: absolute;
  z-index: 10;
  height: 45px;
  width: 45px;
  text-align: center;
  justify-content: center;
  align-content: center;
  display: inline-flex;
  font-size: 10px;
  padding: 0.25em;
  margin: 0.25em;
  background: transparent;
  border: 0 solid red;
  cursor: image('rpg_point.cur') , pointer;
  &:focus {
    color: white;
    outline: 0;
    transform: scale(1.2);
  }
  &:active {
    transform: translateY(2px);
  }
  &.up {
    background-image: image('rpg_up.png');
    top: 10px;
    left: 55px;
  }
  &.down {
    background-image: image('rpg_down.png');
    top: 100px;
    left: 55px;
  }
  &.left {
    background-image: image('rpg_left.png');
    top: 55px;
    left: 10px;
  }
  &.right {
    background-image: image('rpg_right.png');
    top: 55px;
    left: 100px;
  }
}
input {
  &[type="checkbox"],
  &[type="radio"] {
    //opacity: 0;
    position: absolute;
    top: -1em;
    left: -2em;
    
  }
}

label {
  cursor: image('rpg_point.cur') , pointer;
}

.room {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  height: $room-size;
  width: $room-size;
  background-color: transparent;
  display: none;
  border: $wall-width + px solid transparent;
  z-index: 1;
  box-sizing: content-box;
  
  
  transition-delay: 99999999999999s;
  transition-property: top, left;
  transition-duration: 2s;
  transition-timing-function: linear;
  
  &:before {
    //content: attr(data-room);
    position: absolute;
    top: -150px;
    right: 0;
    left: 0;
    text-align: center;
    padding: 0.5em 1em;
    font-family: VT323;
    font-size: 3em;
    color: black;
    @include stroke(2, white)
  }
}

.bed {
  display: block;
  position: absolute;
  height: 64px;
  width: 32px;
  background: image('bed_32-64_01.jpg');
}
.carpet {
  display: block;
  position: absolute;
  height: 64px;
  width: 64px;
  background: image('carpet_64_01.jpg');
}
.stove {
  display: block;
  position: absolute;
  height: 28px;
  width: 32px;
  background: image('stove_32-28_01.jpg');
}
.sink {
  display: block;
  position: absolute;
  height: 28px;
  width: 96px;
  background: image('sink_96-28_01.jpg');
}
.table {
  display: block;
  position: absolute;
  height: 64px;
  width: 32px;
  background: image('table_32-64_01.jpg');;
}
.plant {
  display: block;
  position: absolute;
  height: 32px;
  width: 32px;
  background: image('rpg_plant_32_1.png');
}
.deko1 {
  display: block;
  position: absolute;
  height: 32px;
  width: 32px;
  background: image('rpg_deko_32_1.png');
}
.deko2 {
  display: block;
  position: absolute;
  height: 32px;
  width: 32px;
  background: image('rpg_deko_32_2.png');
}
.basket {
  display: block;
  position: absolute;
  height: 32px;
  width: 32px;
  background: image('rpg_basket_32_1.png');
}
.fireplace {
  display: block;
  position: absolute;
  height: 16px;
  width: 64px;
  background: image('rpg_fireplace_64-16_1.png');
}
.character {
  height: $character-height;
  width: $character-width;
  margin: $character-height/-2 $character-width/-2;
  position: absolute;
  top: 50%;
  left: 50%;
  background: image('character_32_01.jpg');
  background-size: $character-sprite-size;
  background-position: $character-width 0;
  animation-duration: 0.3s;
  animation-iteration-count: infinite;
  animation-timing-function: steps(3);
  z-index: 2;
  
  &:before {
    position: absolute;
    bottom: 100%;
    left: 100%;
    width: 150px;
    text-align: center;
    background: white;
    border-radius: 3px;
    border: 2px solid black;
    padding: 3px;
    font-family: VT323;
    color: black;
    animation: speak-intro 5s linear forwards;
    
  }
  &:after {
    height: 10px;
    width: 20px;
    position: absolute;
    bottom: 100%;
    left: 100%;
    margin: -8px;
    border-radius: 0 0 50% 50%;
    animation: speak-intro 5s linear forwards;
    box-shadow:
     inset -7px 0 0 white,
     inset -9px 0 0 black,
     2px 2px 0 black;
  }

}

.girl {
  height: $character-height;
  width: $character-width;
  position: absolute;
  background: image('rpg_girl.png');
  cursor: image('rpg_speak.png'),pointer;
  background-size: $character-width $character-height;

  &:before {
    position: absolute;
    bottom: 100%;
    left: 100%;
    width: 150px;
    text-align: center;
    background: white;
    color: black;
    border-radius: 3px;
    border: 2px solid black;
    padding: 3px;
    font-family: VT323;
    animation: speak-intro 5s linear forwards;
    
  }
  &:after {
    height: 10px;
    width: 20px;
    position: absolute;
    bottom: 100%;
    left: 100%;
    margin: -8px;
    border-radius: 0 0 50% 50%;
    animation: speak-intro 5s linear forwards;
    box-shadow:
     inset -7px 0 0 white,
     inset -9px 0 0 black,
     2px 2px 0 black;
  }
  
  &:focus {
    &:before {
      content: attr(data-speak);
      animation-name: speak-girl;
    }
    &:after {
      content: '';
      animation-name: speak-girl;
    }
  }

}

.door {
  position: absolute;
  background: blue;
  background-size: 32px 32px;
  
  &.vertical {
    width: $wall-width + px;
    height: 64px;
  }
  &.horizontal {
    height: $wall-width + px;
    width: 64px;
  }
}

.bedroom {
  background: image('floor_32_03.jpg');
  border-image: image("wall_32_02.png") $wall-width*1.5 stretch;
  
  .door {
    background: image('floor_32_03.jpg');
    top: $wall-width*-1px;
    left: 160px;
  }
  .bed {
    top: 0;
    left: 0;
  }
  .plant {
    right: 0;
    top: 0;
    &._2 {
      top: 32px;
    }
  }
  .carpet {
    right: 128px; 
    top: 64px;
     &._2 {
      right: 64px;
      top: 128px;
    }
     &._3 {
      right: 128px; 
      top: 128px;
    }
     &._4 {
      right: 64px;
    }
  }
  .deko2 {
    left: 160px;
    bottom: 0;
     &._2 {
      left: 192px;
    }
    
  }
}
.livingroom {
  background: image('floor_32_01.jpg');
  border-image: image("wall_32_01.png") $wall-width*1.5 stretch;
  .door {
    background: image('floor_32_01.jpg');

    &[for="ra_2"] {
      bottom: $wall-width*-1px;
      left: 160px;
    }
    &[for="ra_3"] {
      left: $wall-width*-1px;
      bottom: 160px;
    }
  }
  .carpet {
    left: 128px;
    top: 64px;
  }
  .girl {
    left: 160px;
    top: 192px;
  }
  .fireplace {
    left: 128px;
    top: 0;
  }
  .plant {
    left: 96px;
    top: 0;
    &._2 {
      left: 192px;
    }
  }
  .deko1 {
    right: 0;
    top: 64px;
  }
  .deko2 {
    left: 160px;
    top: 32px;
  }
  .basket {
    left: 0;
    top: 192px;
  }
}
.kitchen {
  background: image('floor_32_05.jpg');
  border-image: image("wall_32_02.png") $wall-width*1.5 stretch;
  
  .door {
    background: image('floor_32_05.jpg');
    &[for="ra_4"] {
      right: $wall-width*-1px;
      bottom: 160px;
    }
  }
  .stove {
    top: 0;
    left: 0;
  }
  .sink {
    top: 0;
    left: 32px;
  }
  .table {
    bottom: 64px;
    left: 32px;
  }
}
#ra_1 { 
  &:focus {
    ~.room {
     left: calc(50% + #{$room-offset-x} - 160px - #{$character-width/2});
     top: calc(50% - #{$room-offset-y});
     transition-delay: 0s;
    }
  }
  &:checked {
    ~.livingroom {
      display: block;
    }
  }
}

#ra_2 {
  &:focus {
    ~.room {
     left: calc(50% + #{$room-offset-x} - 160px - #{$character-width/2});
     top: calc(50% + #{$room-offset-y});
     transition-delay: 0s;
    }
  }
  &:checked {
    ~.bedroom {
      display: block;
    }
  }
}

#ra_3 { 
  &:focus {
    ~.room {
     top: calc(50% + #{$room-offset-y} - 160px + #{$character-height*1.5});
     left: calc(50% - #{$room-offset-x});
     transition-delay: 0s;
    }
  }
  &:checked {
    ~.kitchen {
      display: block;
    }
  }
}

#ra_4 { 
  &:focus {
    ~.room {
     top: calc(50% + #{$room-offset-y} - 160px + #{$character-height*1.5});
     left: calc(50% + #{$room-offset-x});
     transition-delay: 0s;
    }
  }
  &:checked {
    ~.livingroom {
      display: block;
    }
  }
}


// movement
.right {
  &:active {
    ~ .room {
      transition-delay: 9999s, 0s;
      left: calc(50% - #{$room-offset-x});
    }
    ~ .character {
      animation-name: right;
    }
  }
}
.left {
  &:active {
    ~ .room {
      transition-delay: 9999s, 0s;
      left: calc(50% + #{$room-offset-x});
    }
    ~ .character {
      animation-name: left;
    }
  }
}
.down {
  &:active {
    ~ .room {
      transition-delay: 0s, 9999s;
      top: calc(50% - #{$room-offset-y});
    }
    ~ .character {
      animation-name: down;
    }
  }
}
.up {
  &:active {
    ~ .room {
      transition-delay: 0s, 9999s;
      top: calc(50% + #{$room-offset-y});
    }
    ~ .character {
      animation-name: up;
    }
  }
}

@keyframes down {
  from {
    background-position: 0 0;
  }
  to {
    background-position: $character-width*3 0;
  }
}
@keyframes left {
  from {
    background-position: 0 $character-height*3;
  }
  to {
    background-position: $character-width*3 $character-height*3;
  }
}
@keyframes right {
  from {
    background-position: 0 $character-height*2;
  }
  to {
    background-position: $character-width*3 $character-height*2;
  }
}
@keyframes up {
  from {
    background-position: 0 $character-height*1;
  }
  to {
    background-position: $character-width*3 $character-height*1;
  }
}
// speak

#ch_stove:focus {
  ~.character {
    &:before {
      content: attr(data-stove);
      animation-name: speak-stove;
    }
    &:after {
      content: '';
      animation-name: speak-stove;;
    }
  }
}
#ch_sink:focus {
  ~.character {
    &:before {
      content: attr(data-sink);
      animation-name: speak-sink;
    }
    &:after {
      content: '';
      animation-name: speak-sink;
    }
  }
}

#ch_bed:focus {
  ~.bedroom {
   animation: sleep 5s linear forwards;
  }
  ~.character {
    &:before {
      content: attr(data-bed);
      animation-name: speak-bed;
    }
    &:after {
      content: '';
      animation-name: speak-bed;
    }
  }
}
#ch_table:focus {
  ~.character {
    &:before {
      content: attr(data-table);
      animation-name: speak-table;
    }
    &:after {
      content: '';
      animation-name: speak-table;
    }
  }
}
#ch_fireplace:focus {
  ~.character {
    &:before {
      content: attr(data-fireplace);
      animation-name: speak-fireplace;
    }
    &:after {
      content: '';
      animation-name: speak-fireplace;
    }
  }
}
@mixin speak($object) {
  @keyframes speak-#{$object} {
    0%, 80% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }
}
@keyframes sleep {
  0%, 80% {
    opacity: 0.2;
  }
  100% {
    opacity: 1;
  }
}

@include speak(stove);
@include speak(sink);
@include speak(bed);
@include speak(girl);
@include speak(table);
@include speak(fireplace);





// explain
.info {
  position: absolute;
  top: 30px;
  right: 30px;
  display: block;
  height: 25px;
  width: 25px;
  line-height: 20px;
  text-align: center;
  border-radius: 100%;
  border: 2px solid white;
  background: black;
  color: white;
  font-family: VT323;
}
.explain {
  position: fixed;
  z-index: 999;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background: rgba(0,0,0,1);
  display: flex;
  justify-content: center;
  align-content: center;
  align-items: center;
  font-family: VT323;
  
  .title {
    position: absolute;
    font-size: 60px;
    top: 100px;
    margin: 0;
    text-align: center;
    white-space: nowrap;
    left: 50%;
    transform: translateX(-50%);
    @include stroke(4, white);
    color: black;
    span {
      display: block;
      font-size: 18px;
      margin-top: 10px;
      color: inherit;
      @include stroke(1, white);
      a {
        color: inherit;
        text-decoration: none;
      }
    }
 }

  #ch_play:checked ~ & {
    display: none;
  }
  
  .modal {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 350px;
    color: white;
    border: 8px solid red;
    padding: 20px;
    line-height: 2em;
    background-clip: padding-box;
    background-image: image('modal_bg.png');
    border-image: image("modal_bd.png") 8 repeat;
   @include stroke(2, black);
 }
  .button {
    position: relative;
    display: flex;
    background: image('button-play.png');
    background-size: 100% 100%;
    color: white;
    font-weight: 700;
    padding: 0.25em;
    text-align: center;
    justify-content: center;
    font-family: VT323;
    cursor: pointer;
    &:active {
      top: 2px;
    }
  }
  .key {
    font-size: 0.5em;
    line-height: normal;
    display: inline-block;
    vertical-align: middle;
    padding: 0.5em;
    text-transform: uppercase;
    border-radius: 3px;
    border-style: solid;
    border-width: 1px 3px 8px 3px;
    border-color: #aaa #aaa #777 #aaa;
    background: white;
    color: #222;
    font-family: sans-serif;
    text-shadow: none;
  }
}


audio {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 60px;
  border: 8px solid transparent;
  padding: 5px;
  background-clip: padding-box;
  background-image: image('modal_bg.png');
  border-image: image("modal_bd.png") 8 repeat;

  &::-webkit-media-controls-panel {
    background: none;
    -webkit-appearance: none;
  }
  &::-webkit-media-controls-current-time-display {
    background: none;
    font-family: VT323;
    @include stroke(2, black);
    color: white;
    font-size: 16px;
  }

 
}
            
          
!

JavaScript

            
              
            
          
!
999px
Loading ..................