Tune-Town 2

Tune-Town 2 preview image

1 collaborator

Turtlezero2-white-048 James Steiner (Author)

Tags

(This model has yet to be categorized with any tags)
Visible to everyone | Changeable by everyone
Model was written in NetLogo 6.2.2 • Viewed 51 times • Downloaded 1 time • Run 0 times
Download the 'Tune-Town 2' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


# TOON-TOWN ```by James P. Steiner```

## Build a Musical Highway! ##

DRAW ROADS. PLACE PROPS. PLACE CARS. CLICK DRIVE.

USE ONE-WAY SIGNS TO CONTROL TRAFFIC FLOW.

USE PORTALS TO JUMP BETWEEN NEIGHBORHOODS

## Featuring Steiner's Over-Engineered Mouse Driver for NetLogo 2022 ##

### MOUSE DRIVER NLS FILE CODE IS INCLUDED IN MODELING COMMONS .ZIP DOWNLOAD ###

## Introduction ##

TUNE-TOWN is an incredibly elaborate demonstration of the mouse driver.

TUNE-TOWN uses Click, Drag, Drop, Long-Click, Patch-Enter, Patch-Exit, Drag-Enter, Drag-Exit, Click-End, Click-Start.

TUNE-TOWN also demonstrates an implementation of Turtle-based Toolbar, Buttons, and Button Icons.

## Mouse Driver ##

This module provides a framework for handling complex mouse operations. We find ourselves writing the same mouse code over and over. Some times it's quite simple, other times, complex. Beginners with netlogo may stumble through the mouse interface code, stealing time, effort, and energy from the novel and interesting parts of their simulation.

This aims to fix that.

## How it works, part 1

This framework is patch-centric. A breed of turtle follows the mouse pointer, and triggers events based on position and the state of the mouse button.

Patches listen for events sent by the mouse code, using event listeners added to their listener collection, and run their own code in response. Patches can listen on behalf of other agents.

The pointer also exposes some properties useful for your other mouse-interactions.

## What is if for?

For any model that needs mouse-based user interaction where more simple techniques won't do, or where the modeler doesn't wish to write their own mouse code.

## Extending to other kinds of agents

There are many ways to do it. One is to allow patches to pass events on to associated turtle-based controls, as in the tune-town demo

## How it works, part 2

### The pointer raises events and updates pointer-related properties

Your code can simply monitor and use those properties as it wishes.

This may make sense where it's the pointer, more than the patches or turtles,

are driving events.

### Patches Own Event Listeners.

The pointer also dispatches control to patches that possess event listeners matching the currently occuring event. Usually this is focused on the patch directly under the pointer.

Patches that need to respond to events add an "event listener" to their own list of listeners (the m:LISTENERS list) using the m:LISTEN-FOR command. An event listener names the event, and contains code that is run by the patch when the event occurs, in the form of an anonymous command procedure.

### The Mouse Tells the Patch to Run the Listener

When a mouse event occurs, the m:mouse object tells the appropriate patch to run the event code for that event, if it is defined.

## What about turtle events?

At this time, turtle events would be handled by writing patch event code that manipulates the turtle(s) standing on the patch (or elsewhere?).

## How to use the Mouse Driver

(1.) add the mouse driver file to an __INCLUDE section.

```__INCLUDES [ "mouse-driver-2022-2.nls" ]```

(2.) in your GO (or some other forever button, like EDIT or INTERACTION), call M:GO-MOUSE

```

to interact ;; forever button

M:GO-MOUSE

every 1 / 30

[ update-animation ]

end

```

(3.) Add listeners to patches as needed using the M:LISTEN-FOR command on patches.

For example:

```

ask patches

[

m:listen-for e:click [ -> set pcolor pcolor + 1 ]

m:listen-for e:drag [ -> set pcolor pcolor + 1 ]

]

```

## Doing Drag / Drop

The Drag-Start event occurs when the pointer button is down, and the pointer leaves a patch, this also alerts the mouse that dragging is occuring.

If there is no drag-start event action defined for the current patch, then dragging does not start--instead click-move click and click-end occur

If a long-click started, then long-click-drag events may occur.

## Supported Mouse Events

The mouse driver module supports the following mouse events

```

set e:null-event m:new-event "null-event" "this is not an event, but a placeholder where there is no event"

set e:drag-exit m:new-event "drag-patch-exit" "mouse entered the patch while dragging"

set e:drag-enter m:new-event "drag-patch-enter" "mouse entered the patch while dragging"

set e:drag-start m:new-event "drag-start" "the mouse left the patch with button down, starting a drag"

set e:drag-idle m:new-event "drag-idle" "the mouse is not moving but over this patch during a drag"

set e:drag-move m:new-event "drag-move" "the patch began dragged does this"

set e:drag-drop m:new-event "drag-drop" "the patch being dragged does this"

set e:drag-catch m:new-event "drag-catch" "the patch under the mouse does this"

set e:drag-cancel m:new-event "drag-cancel" "ends and cleans up the current drag operation early"

set e:click-start m:new-event "click-start" "mouse button was just pressed. aka mouse-down"

set e:click-idle m:new-event "click-idle" "continuous. mouse button is pressed, mouse is not moving"

set e:long-click-start m:new-event "long-click-start" "mouse button down long enough to enable long-press"

set e:click-move m:new-event "click-move" "mouse moves while the button is pressed -- is not dragging"

set e:long-click-cancel m:new-event "long-click-cancel" "mouse moved after a long-press was activated, but before the button was released"

set e:click-enter m:new-event "click-enter" "mouse enters a new patch while the button is pressed"

set e:click-exit m:new-event "click-exit" "mouse exits a patch while the button is pressed"

set e:long-click m:new-event "long-click" "mouse button released after a while"

set e:click m:new-event "click" "mouse-button was released while on the same patch where the button was pressed"

set e:click-end m:new-event "click-end" "final event when the mouse-button is released, aka mouse-up"

set e:mouse-move m:new-event "mouse-move" "pointer moved"

set e:mouse-idle m:new-event "mouse-idle" "pointer is not moving, button up"

set e:patch-enter m:new-event "patch-enter" "pointer entered a patch"

set e:patch-exit m:new-event "patch-exit" "pointer left the patch"

set e:screen-enter m:new-event "screen-enter" "pointer entered the world view"

set e:screen-exit m:new-event "screen-exit" "pointer left the world view"

set e:offscreen-idle m:new-event "offscreen-idle" "pointer remains off the world view"

;; aliases doubles this event

set e:mouse-down e:click-start

set e:mouse-up e:click-end

set e:drag e:drag-enter

set e:hover e:mouse-idle

```

Theoretically, other kinds of events could be added.

## NETLOGO FEATURES

# Includes

The mouse-driver is in a self-contained .nls file that lets it implement its own globals, breeds, -own variables, commands, and reporters, without the modeler needing to integrate these things into their own model code. This reduces opportunities to introduce bugs into the model code or the event handling code.

# Breeds

Uses a distinct breed to both display the mouse pointer (if desired) and manage the event code.

# Breed and Patch Variables

Uses breed and patch variables added by the module,

Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

extensions [ sound dialog]
__includes [ "mouse-driver-2022-2.nls" ]

globals [
  tempo
  velocity
  beats-per-measure
  last-note ;; not really used yet

  prototype-list
  prototype-set

  is-playing?

  field-margin
  field-top
  field-patches

  erase?

  all-sights
  prior-prop

  dragroot
  dragleaf
  dragset

  curr-button
  prior-button

  curr-prop-shape
  curr-prop-instrument
  drawing-mode ; "CAR" "ROAD" "prop" erase" "portal" "one-way"

  #prop-picker

  selected-prototype

  go-button
  stop-button

  note-labels

]

turtles-own [ base-color
]

breed [ toolbars  toolbar ]
breed [ controls control ]
breed [ icons icon ]
breed [ labelers labeler ]
breed [ newthings newthing]
breed [ roads road ]
breed [ roadsigns roadsign ]
breed [ props prop ]
breed [ cars car ]
breed [ portals portal ]
breed [ prototypes prototype ]
breed [ markers marker ]
breed [ staves staff ]
directed-link-breed [ marker-links marker-link ]
directed-link-breed [ portal-links portal-link]
portal-links-own [ expires ]

toolbars-own
[ slotx sloty padding gap button-spacing left-margin top-margin right-margin ]

controls-own
[ control-name
  control-type
  name
  my-icon
  my-labeler
  my-prototype
  prop-id
  control-click-action
  control-tooltip

  kind       ;; drum or note
  instrument ;; instrument name
  note       ;; keynumber 0-127
  new-note   ;; used in UI
  duration   ;; seconds

  base-color
  ]

icons-own [
  icon-shape
  my-control
  base-color
]

labelers-own [ my-control
  base-color
  text ]

newthings-own
[ new-breed
  base-color
]

patches-own
[ base-pcolor
]

roads-own
[ style
  sights
  kind ;; (blank)default; portal
  base-color
]
portals-own
[ destination
  pulse
  base-color
]

props-own
[ prop-id
  my-prototype
  my-button
  name
  kind
  instrument ;; string instrument name
  note       ;; keynumber 0-127
  new-note
  duration   ;; seconds
  base-velocity   ;; base velocity
  base-color ;; color when not playing
   ;; anon command to play correct sound
  my-sound-command

  prop-shape
]

prototypes-own
[ prop-id
  name
  kind
  instrument ;; string instrument name
  note       ;; keynumber 0-127
  duration
  base-velocity
  my-sound-command
  my-control
  icon-shape
  prop-shape
  my-button
]

roadsigns-own
[ name
  kind
  my-action
  base-color
]

cars-own
[ ; base-color
  next-road
  road-head
  exit-head ;; road-head or +/- 90 for turns
  next-exit-head
  sights ;; props visible beside car
  start-position
  start-heading
  sight-patch
  sight-heading
  step
  base-color
]

markers-own
[ my-snap
  drag-axis-x ;; used to force drag marks to stay on a certain axis
  drag-axis-y
  base-color
]

; m:controls-own [ kind instrument ]

to-report filename report "tune-town-2022-prop-list.txt" end

to-report field-color report green + random-float 4 - 2 end

to-report border-color report gray end

to-report icon-regular-color report gray + 2 end

to-report icon-hover-color report white - 1 end

to-report icon-press-color report lime end

to-report icon-selected-color report yellow + 2 end

to-report icon-spacing report 2 end

to-report button-regular-size report 3 end

to-report icon-regular-size report icon-spacing - 6 / patch-size end

to-report icon-hover-size report icon-spacing + 3 / patch-size end

to-report icon-press-size report icon-regular-size * .80 end

to-report icon-selected-size report icon-regular-size + 2 / patch-size end

to-report prop-hit-color report white end

to-report road-color report gray end

to-report intersection-color report turquoise end

to-report hot-color report sky end

to-report cold-color report yellow - 2   end

to-report no-tasks report [] end

to no-action end

to choose-DRUM
  set is-playing? false
  dialog:user-one-of (word "Choose a new sound for " control-name ".") sound:DRUMs
  [ selection ->
    output-write selection
    if selection != false [ set instrument selection ] ]
end 

to startup
  __setup
end 

to send-message [ $text ]
  ask patch (max-pxcor - 10) max-pycor [ set plabel $text ]
end 

to Play-sample [ #note ]
  ifelse
  ( kind = "DRUM" )
  [ sound:play-DRUM instrument velocity ]
  [ ;;sound:play-note instrument keynumber velocity duration
    sound:play-note instrument #note velocity duration
  ]
end 

to play-my-sound
   run my-sound-command
end 

to-report q [ string ]
  report (word "\"" string "\"")
end 

to-report pad-right [ string width ]
  report substring (word string "                                        ") 0 width
end 

to-report pad-left [ string width ]
  report reverse substring (word reverse (word string) "                                        ") 0 width
end 

to props-list-generator
  let i 0
  let s " "
  let pad1 2 + max map length prop-shapes
  let pad2 2 + max map length sound:instruments
 output-print "["
  let DRUM-index 0
  foreach prop-shapes
  [ fullname ->
    let nickname (substring fullname 3 length fullname)
    let inst item DRUM-index sound:DRUMs
   output-print ( word s
      s "["
      s pad-left i 3
      s pad-right q nickname pad1

      s q "DRUM"
      s pad-right q inst pad2
      s 64
      s "]"
    )
    set i i + 1
    set DRUM-index DRUM-index + 1
    ;; OPEN HI HAT MIDI SOUND is BROKEN on MY (and other) Computers
    ;; it will cause problems (like not sounding, or never turning off)
    ;; sklip it!
    if DRUM-index = 11 [ set DRUM-index DRUM-index + 1 ]
  ]
 output-print "]"
end 

to-report load-prototype-list
  ;; load prototype-data into prototype-list
  let $list []
  carefully
  [ file-open filename
    set $list file-read
    file-close
    ;; correct item numbering issues that arise
    ;; from manually editing the arrangement of props
  ]
  [ file-close
    print error-message
    user-message "Unable to load props"
    set $list prototype-list-default
  ]
  report $list
end 

to setup-prototypes
  let $list load-prototype-list
  ( foreach $list (range length $list)
    [ [ row index ] ->
      create-prototypes 1
      [ set prop-id index
        set name item 1 row
        set kind uppercase item 2 row
        set instrument item 3 row
        set base-velocity 64
        set note 60 ;; middle C
        set duration 0.25
        set prop-shape ( word "_p:" name )
        set icon-shape ( word "_p:" name )
        carefully [ set shape "circle" set shape prop-shape ][]
        ifelse kind = "DRUM" [ set my-sound-command [-> sound:play-drum instrument velocity ] ]
        ; sound:play-note instrument keynumber velocity duration
        [ set my-sound-command [-> sound:play-note instrument note velocity duration ] ]
        hide-turtle
      ]
    ]
  )
  set prototype-list sort-on [ prop-id ] prototypes
end 

to-report  format-prototype-list
  let id-len 4
  let name-len 3 + max [ length name ] of prototypes
  let kind-len 3 + max [ length kind ] of prototypes
  let inst-len 3 + max [ length instrument ] of prototypes
  let num-len 4
  let -- "  "
  let table-out "[\n"

  ( foreach (sort-on [ prop-id ] prototypes) (range count prototypes)
    [ [ prop-in index ] ->
      let row-out [(word
        --
        "["
        -- pad-left index id-len
        -- pad-right (q name) name-len
        -- pad-right (q kind) kind-len
        -- pad-right (q instrument) inst-len
        -- "]\n"
      )] of prop-in
      set table-out (word table-out row-out)
    ]
  )
  set table-out (word table-out "]\n")
  report table-out
end 

to save-prop-list
  let new-list format-prototype-list
  carefully
  [ file-close-all
    if file-exists? filename
    [ file-delete filename
    ]
    file-open filename
    file-type new-list
    file-close
  ]
  [ file-close-all
    print error-message
    user-message (word "Unable to save prop list '" filename "'")
  ]
end 

to-report prototype-list-default
  report
[
  [     0  "cat"              "DRUM"   "OPEN TRIANGLE"          64  0 0 ]
  [     1  "cow"              "DRUM"   "COWBELL"                64  0 0]
  [     2  "dog"              "DRUM"   "MUTE CUICA"             64  0 0]
  [     3  "frog"             "DRUM"   "LONG GUIRO"             64  0 0]
  [     4  "moose"            "DRUM"   "CRASH CYMBAL 2"         64  0 0]
  [     5  "crate"            "DRUM"   "PEDAL HI HAT"           64 0 0 ]
  [     6  "barrel"           "DRUM"   "ACOUSTIC BASS DRUM"     64  0 0]
  [     7  "garbage can"      "DRUM"   "CHINESE CYMBAL"         64  0 0]
  [     8  "hydrant"          "DRUM"   "SPLASH CYMBAL"          64  0 0]
  [     9  "mailbox"          "DRUM"   "CRASH CYMBAL 1"         64  0 0]
  [    10  "pram"             "DRUM"   "LOW CONGA"              64  0 0 ]
  [    11  "box-1"            "DRUM"   "LOW TOM"                64  0 0]
  [    12  "box-2"            "DRUM"   "LOW MID TOM"            64  0 0]
  [    13  "box-3"            "DRUM"   "HI MID TOM"             64  0 0]
  [    14  "box-4"            "DRUM"   "HI TOM"                 64  0 0]
  [    15  "box-5"            "DRUM"   "HI FLOOR TOM"           64  0 0]
  [    16  "box-6"            "DRUM"   "MARACAS"                64  0 0]
  [    17  "brick-wall-1"     "DRUM"   "MARACAS"                64  0 0]
  [    18  "brick-wall-2"     "DRUM"   "MARACAS"                64  0 0]
  [    19  "brick-wall-3"     "DRUM"   "MARACAS"                64  0 0]
  [    20  "tree"             "DRUM"   "CABASA"                 64  0 0]
  [    21  "evergreen"        "DRUM"   "LOW WOOD BLOCK"         64  0 0]
  [    22  "cactus"           "DRUM"   "HI WOOD BLOCK"          64  0 0]
  [    23  "flower"           "DRUM"   "ACOUSTIC SNARE"         64  0 0]
  [    24  "plant-1"          "DRUM"   "VIBRASLAP"              64  0 0]
  [    25  "plant-2"          "DRUM"   "RIDE CYMBAL 2"          64  0 0]
  [    26  "plant-3"          "DRUM"   "HI BONGO"               64  0 0]
  [    27  "plant-4"          "DRUM"   "LOW BONGO"              64  0 0]
  [    28  "fisher"           "DRUM"   "HI MID TOM"             64  0 0]
  [    29  "police"           "DRUM"   "LONG WHISTLE"           64  0 0]
  [    30  "cross-guard"      "DRUM"   "OPEN HI CONGA"          64  0 0]
  [    31  "road-crew"        "DRUM"   "HI TIMBALE"             64  0 0]
  [    32  "thief"            "DRUM"   "HI TIMBALE"             64  0 0]
  [    33  "service"          "DRUM"   "MARACAS"                64  0 0]
  [    34  "lumberjack"       "DRUM"   "MARACAS"                64  0 0]
  [    35  "doctor"           "DRUM"   "MARACAS"                64  0 0]
  [    36  "ghost"            "DRUM"   "OPEN CUICA"             64  0 0]
  [    37  "traffic-cone"     "DRUM"   "TAMBOURINE"             64  0 0]
  [    38  "stop-sign"        "DRUM"   "HI AGOGO"               64  0 0]
  [    39  "yield-sign"       "DRUM"   "LOW AGOGO"              64  0 0]
  [    40  "exit-sign"        "DRUM"   "RIDE BELL"              64  0 0]
  [    41  "speed-limit-25"   "DRUM"   "LOW WOOD BLOCK"         64  0 0]
  [    42  "speed-limit-55"   "DRUM"   "HI WOOD BLOCK"          64  0 0]
  [    43  "flag"             "DRUM"   "HAND CLAP"              64  0 0]
]
end 

to redraw-neighbor-roads  ask roads-on neighbors4 [ apply-road-shape ] end

to update-pcolor [ hue ]
  set base-pcolor hue
  set pcolor hue
end 

to update-base-color [ hue ]
  set base-color hue
  set color base-color
end 

to set-road-color
  ifelse (is-grid-intersection?)
      [ update-base-color intersection-color ]
      [ update-base-color road-color ]
end 

to-report is-field-patch?
  report ( (pxcor >= min-pxcor + 1) and (pxcor <= max-pxcor - 1) and (pycor >= min-pycor + 1) and  (pycor <= field-top))
end 

to set-field-color
  ifelse (is-field-patch?)
  [
    ifelse (is-grid-intersection?)
    [ update-pcolor black + 2]
    [ update-pcolor black ]
  ]
  [ update-pcolor gray + (random 6) / 5   set plabel-color black ]
end 

to make-road-here [ event ]
  if any? props-here
  [ ask props-here [ die ]
  ]
  if not any? roads-here
  [ sprout-roads 1
    [ carefully [ set shape (word road-style "new") ] []
      set size 1
      set breed roads
      set heading 0
      set kind ""
      ;; animate-new-item
      set-road-color
      set-field-color
      apply-road-shape
      ]
    redraw-neighbor-roads
  ]
end 

to apply-road-shape
  let new-shape shape
  let others-set roads-on neighbors4
  let others sort-on [ towards myself ]  others-set
  ( ifelse
    ( length others = 0 )
    [ set new-shape ( word road-style "island" ) ]
    ( length others = 1 )
    [ set new-shape ( word road-style "u-turn" )
      set heading 180 + towards first others
    ]
    ( length others = 2 )
    [ set heading 180 + towards first others
      let d subtract-headings heading towards last others
      ( ifelse
        ( d =   0 ) [ set new-shape ( word road-style "straight" ) ]
        ( d = -90 ) [ set new-shape ( word road-style "right" ) ]
        ( d =  90 ) [ set new-shape ( word road-style "left" ) ]
      )
    ]
    ( length others = 3 )
    [ set new-shape ( word road-style "tee" )
      set heading  sum [ -1 * towards myself ] of others-set
      ; if heading = 0 or heading = 180 [ rt 180 ]
    ]
    ( length others = 4 )
    [ set new-shape ( word road-style "crossing" ) ]
  )
  set shape new-shape
end 

to-report prop-shapes
  report sort filter [$name -> 0 = position "_p:" $name ] shapes
end 

to-report random-prop
  report one-of prop-shapes
end 

to-report can-prop-go-here?
  report (any? roads-on neighbors4) and not (any? roads-here or any? props-here )
end 

to make-prop-here [ $prototype $event]
  ;; run by patch
  ifelse ($event = e:drag and erase?) or ($event = e:click and any? props-here )  [ ask props-here [ die ] ]
  [
    if can-prop-go-here?
    [ let here self
      ask $prototype
      [ hatch-props 1
        [ move-to here
          set shape prop-shape
          set color black
          set note last-note
          if kind = "NOTE" [ set label (word (item (note mod 12) note-labels) "-" int (note / 12 - 1) ) ]
          show-turtle
          run my-sound-command
        ]
      ]
    ]
  ]
end 

to make-roadsign-here [ $kind ]
  if can-roadsign-go-here? $kind
  [ ifelse any?  roadsigns-here
    [ let this-sign one-of roadsigns-here
      ask this-sign
      [ if kind = "one-way"
        [ rt 90 sfx-click ]
      ]
    ]
    [ sfx-click
      sprout-roadsigns 1
      [
        set name $kind
        set-shape-safely (word "_sign_" $kind)
        set kind $kind
        set heading 0
        update-base-color white
        if kind = "one-way"
        [ set heading [ heading ] of one-of roads-here ]
      ]
    ]
  ]
end 

to set-shape-safely [ shape-name ]
  if position shape-name shapes != false
  [ set shape shape-name ]
end 

to make-portal-here
  ifelse any? portals-here
  [ if user-yes-or-no? "Delete portal? This will break all links to this portal!"
    [ sfx-whoosh
      ask portals-here [ die ]
    ]
  ]
  [ if can-portal-go-here?
    [ sfx-whoosh
      sprout-portals 1
      [ set shape "_sign_portal"
        set color who * 10 + red + 2
        set size 1.5
        set pulse 1
        set destination nobody
        if any? other portals
        [ set destination one-of other portals
          create-portal-link-to destination
          [ set thickness 3 / patch-size
            set shape "portal-link-to"
            set expires 20
            set color [ color ] of myself
          ]
        ]
      ]
      select-portal-destination
    ]
  ]
end 

to-report can-portal-go-here?
  report any? roads-here and not any? portals-here
end 

to-report can-one-way-go-here?
  report can-roadsign-go-here? "one-way"
end 

to-report can-roadsign-go-here? [ $kind ]
  report any? roads-here
end 

to make-one-way-here
  make-roadsign-here "one-way"
end 

to-report can-car-go-here?
  report ( any? roads-here )
end 

to sfx-beep-beep
  sound:play-note-later .2 "FIFTHS" 72 velocity .1  sound:play-note "FIFTHS" 72 velocity .1
end 

to sfx-click
  sound:play-DRUM "SIDE STICK" velocity
end 

to sfx-wah-wah
  sound:play-note-later .2 "trombone" 45 velocity .3 sound:play-note "trombone" 49 velocity .3
end 

to sfx-whoosh
  sound:play-DRUM "VIBRASLAP" velocity
end 

to make-car-here [ event ]
  ;; run by pointer
  ifelse (erase? or event = e:click) and any? cars-here
  [ ask cars-here [ die ] sfx-wah-wah ]
  [ if ( can-car-go-here? )
    [ sprout-cars 1
      [
        set start-position patch-here
        set shape "_car_3_90"
        set base-color color
        set heading 0
        set sights no-turtles
        if any? roads-on neighbors4
        [ face one-of roads-on neighbors4 set shape (word "_car_3_" heading) ]
        set start-heading heading
      ]
       sfx-beep-beep
    ]
  ]
end 

to _setup-field-patches
  output-print "defining field-patches"
  set field-patches patches with [ is-field-patch? ]
  output-print "defining events for field-patches"
  let n 0
  ask field-patches
  [
    output-type "X"
    set n n + 1
    if ( n mod ( world-width - 4 ) = 0 ) [ output-print ""]
    m:listen-for e:click-start [ -> do-click-start  ]
    m:listen-for e:click       [ -> do-click  ]
    m:listen-for e:drag-start  [ -> do-drag-start ]
    m:listen-for e:drag-enter  [ -> do-drag-enter ]
    m:listen-for e:drag-exit   [ -> do-drag-exit  ]
    m:listen-for e:drag-drop   [ -> do-drag-drop ]
    m:listen-for e:patch-enter [ -> do-patch-enter ]
    m:listen-for e:patch-exit  [ -> do-patch-exit ]
    m:listen-for e:drag-cancel [ -> do-drag-cancel ]
    m:listen-for e:long-click-start [ -> do-long-click-start ]
  ]
  output-print ""
  output-print "patch events added."

  output-print "coloring field-patches"
  ask patches
  [ set-field-color
  ]
end 

to draw-prop-enter
  ifelse can-prop-go-here?
  [ set pcolor green
    send-message "CLICK TO PLACE prop HERE"
  ]
  [
    set pcolor red - 2
    send-message "PLACE PROPS NEXT TO ROADS"
  ]
end 

to draw-car-enter
  ifelse can-car-go-here?
  [ set pcolor green
    send-message "CLICK TO PLACE CAR HERE"
  ]
  [
    set pcolor red - 2
    send-message "PLACE CARS ON ROADS"
  ]
end 

to draw-portal-enter
  ifelse can-portal-go-here?
  [
    ; set pcolor green
    m:set-color green
    send-message "CLICK TO PLACE PORTAL HERE"
  ]
  [ ifelse any? portals-here
    [ ; m:set-shape "_sign_portal"
      send-message "CLICK TO REMOVE PORTAL. LONG-CLICK TO CHANGE DESTINATION."
      ;; show link to other portal, if a portal is here
      ask portals-here
      [ if not any? my-out-links
        [ if is-portal? destination
          [ create-portal-link-to destination
            [ set shape "portal-link-to"
              set thickness 4 / patch-size
              set color [ color ] of end2
              set expires 100
            ]
            ask destination
            [ create-portal-link-to myself
              [ set shape "portal-link-from"
                set thickness 4 / patch-size
                set color [ color ] of end2
                set expires 100
              ]
            ]
          ]
        ]
      ]
    ]
    [
      set pcolor red - 2
      ; m:set-shape "NO"
      send-message "PLACE PORTALS ON ROADS"
    ]
  ]
end 

to draw-oneway-enter
end 

to do-patch-enter
  ifelse is-field-patch?
  [ m:show
    ( ifelse
      ( drawing-mode = "PROP" ) [ draw-prop-enter ]
      (drawing-mode = "CAR" ) [ draw-car-enter ]

      ( drawing-mode = "PORTAL" ) [ draw-portal-enter ]
      ( drawing-mode = "ONEWAY" ) [ ifelse can-one-way-go-here?  [ set pcolor green send-message "CLICK TO PLACE ONEWAY HERE" ] [ set pcolor red - 2  send-message "PLACE SIGNS NEXT TO ROADS"] ]

    [ set pcolor green ]
    )
  ]
  [ ; m:hide
  ]
end 

to do-patch-exit
  if is-field-patch?
  [
    set pcolor base-pcolor
  ]
  ask portals-here [ ask my-portal-links [ die ] ]
end 

to-report slotx++
  report  slotx  ;; the toolbar
end 

to-report sloty++
  let sloty-- sloty
  set slotx slotx + button-spacing
  if slotx >= right-margin
  [
    set slotx left-margin
    set sloty sloty - button-spacing
  ]

  report sloty--
end 

to _setup-toolbar
  let this-toolbar nobody
  create-toolbars 1
  [ hide-turtle
    setxy min-pxcor min-pycor
    set button-spacing 2
    set padding 0
    set left-margin  min-pxcor  + ( padding  )
    set right-margin max-pxcor  - ( padding  )
    set top-margin   max-pycor  - ( padding  ) - 1
    set slotx left-margin
    set sloty top-margin
    set this-toolbar self
  ]

    ;; btndata format is:
  ;; [1 "box-2" "DRUM" "BASS DRUM 1" Velocity]
  ;; let this nobody
  let btninfo []


  ask this-toolbar
  [
    make-spacer
    make-spacer
    ask make-button "TOGGLE" "_icon_grid"       "TOGGLE GRID MARKS"        [ -> set-gridspacing m:hide] [ set control-name "GRID" ask my-icon [ set label precision beats-per-measure 0 ] ]
    ask make-button "CMD" "_icon_road"       "BUILD ROADS MODE"            [ -> set drawing-mode "ROAD" m:set-shape "m-pointer-3" m:show]  [ ]
    ask make-button "CMD" "_icon_car"        "ADD/REMOVE CARS MODE"        [ -> set drawing-mode "CAR"  m:set-shape "_tool_car"   m:show ]   [ ]
    ask make-button "CMD" "_icon_erase"      "ERASER MODE"                 [ -> set drawing-mode "ERASE" m:set-shape "_tool_erase" m:show ] [ ]
    make-spacer
    ask make-button "CMD" "_icon_drive"      "START DRIVING"               [ -> set is-playing? true ] []
    ask make-button "CMD" "_icon_stop"       "STOP DRIVING"                [ -> set is-playing? false  ] []
    ask make-button "CMD" "_icon_rewind"     "RETURN TO START"             [ -> rewind-cars            ] []
    make-spacer
    ask make-button "VALUE"   "_icon_vol-plus"   "PLAY MORE LOUDLY"               [ -> get-louder]  []
    ask make-button "DISPLAY" "_icon_speaker"    "CURRENT VELOCITY (LOUDNESS)" [-> set velocity 64 ] [ set control-name "VELOCITY" ask my-icon [ set label precision velocity 0 ]  ]
    ask make-button "VALUE"   "_icon_vol-minus"  "PLAY MORE QUIETLY"             [ ->  get-softer ]  []
    ; make-spacer
    ask make-button "VALUE"   "_icon_tempo-down" "DRIVE MORE SLOWLY"             [ ->  get-slower ] []
    ask make-button "DISPLAY" "_icon_speed"      "CURRENT TEMPO (SPEED)"       [-> ] [ set control-name "TEMPO" ask my-icon [ set label precision tempo 0 ]  ]
    ask make-button "VALUE"   "_icon_tempo-up"   "DRIVE MORE QUICKLY"            [ ->  get-faster  ] []
    make-spacer
    ask make-button "EFX"     "_icon_one-way"   "ADD ONE-WAY SIGN"           [ ->  set drawing-mode "ONEWAY" m:set-shape "m-pointer-3" ][]
    ask make-button "PORTAL"  "_icon_portal"   "ADD PORTAL TO ROAD"          [ -> set drawing-mode "PORTAL" m:set-shape "_sign_portal" ][]
    make-spacer
    ask make-button "CMD"     "_icon_question"   "ADD/REMOVE PROPS MODE, select random prop"       [ -> random-prop-button ]       [ set control-name "RANDOPROP" ]
    ;;
    ;; now make the buttons for all the props in the props-list
    foreach prototype-list [ p -> make-prop-button p ]
    set field-top sloty - button-spacing - 1
  ]
end 

to random-prop-button
  let rando one-of controls with [ control-type = "PROP" ]
  ask rando [ run control-click-action ]
end 

to set-gridspacing
  set beats-per-measure beats-per-measure + 1
  if beats-per-measure >= 5 [ set beats-per-measure 0 ]
  if beats-per-measure = 1 [ set beats-per-measure 2 ]
  ask roads [ set-road-color ]
  ask field-patches [ set-field-color ]
  ask [ my-icon ] of one-of (Controls with [ control-name = "GRID" ] )
  [ set label precision beats-per-measure 0 ]
end 

to get-softer
   set velocity ifelse-value ( velocity <= 0) [ 0 ] [ (max (list 1 int (velocity *  .8 ))) ]
  ask [ my-icon ] of one-of (Controls with [ control-name = "VELOCITY" ] )
  [ set label precision velocity 0 ]
end 

to get-louder
  set velocity ifelse-value ( velocity < 1) [ 1 ] [ (min (list 127 (velocity + max (list 1 int (velocity * .1 ))))) ]
  ask [ my-icon ] of one-of (Controls with [ control-name = "VELOCITY" ] )
  [ set label precision velocity 0 ]
end 

to get-faster
  set tempo tempo + 10
  ask [ my-icon ] of one-of (Controls with [ control-name = "TEMPO" ] )
  [ set label precision tempo 0 ]
end 

to get-slower
  set tempo tempo - 10
  ask [ my-icon ] of one-of (controls with [ control-name = "TEMPO" ] )
  [ set label precision tempo 0 ]
end 

to rewind-cars
  ask cars [ move-to start-position set heading start-heading]
end 

to make-spacer
  ask make-button "GAP" "_icon_blank" "" [->] [ set shape "__blank__"]
end 

to-report  char-to-upper [ c ]
  let lower  "abcdefghijklmnopqrstuvwxyz"
  let upper  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  let p position c lower
  report ifelse-value ( p = false ) [ c ] [ item p upper ]
end 

to-report split-string [ s ]
  report n-values length s [ i -> item i s ]
end 

to-report uppercase [ s ]
  report reduce word map  char-to-upper (split-string s)
end 

to-report my-linked-patches
  report patches at-points [ [ 0 0 ] [ 0 -1 ] [ 1 0 ] [ 1 -1 ] ]
end 

to do-long-press-prop
  m:cancel-long-click
  let prompt ""
  let choices []
  let no-change "- NO CHANGE -"
  ifelse( kind = "DRUM" )
  [ set prompt (word "Select a new DRUM for " control-name)
    set choices fput no-change ifelse-value ( sort-inst-list? ) [ sort DRUM-list ] [ DRUM-list ]
  ]
  [ set prompt (word "Select a new instrument for " control-name)
    set choices fput no-change instrument-list
  ]

  let new-inst user-one-of prompt choices
  if ( new-inst != no-change and first new-inst != "-" )
  [
    ask my-prototype [ set instrument new-inst ]
    set instrument new-inst
    update-prop-tooltip
    save-prop-list
  ]
end 

to do-long-click-start
  if any? portals-here [ select-portal-destination ]
end 

to select-portal-destination
  m:cancel-long-click
  let this one-of portals-here
  ask this
  [ let pSET other portals
    if any? pset
    [ set color white
      let pLIST sort pSet
      let pnum 0
      let clabels [ "red" "yellow" "green" "blue" "purple" "orange" "turquoise" "pink" ]
      let alabels [ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ]
      let ccolors [ red yellow green blue violet orange turquoise pink ]
      (foreach plist range length plist
        [ [ pp index] ->
          ask pp
          [ set color item (index mod (length ccolors)) ccolors
            set label-color item (index mod (length ccolors)) ccolors
            set label (word ITEM (index mod (length alabels)) alabels index "-" item (index mod (length clabels)) clabels)
          ]
      ])
      create-portal-links-to pset
      [ set label [ label ] of other-end
        set color [ color ] of other-end
        set label-color color
        set shape "portal-link-to"
        set thickness 3 / patch-size
        set expires 20
      ]
      create-portal-links-from pset
      [ set label [ label ] of other-end
        set color [ color ] of other-end
        set label-color color
        set shape "portal-link-from"
        set thickness 2 / patch-size
        set expires 20
      ]
      ;; player chooses a destiantion
      let label-list fput "NO DESTINATION" sort [ label ] of pset
      let pchoice user-one-of "select destination of this portal" label-list
      ask this [ set destination one-of portals with [ label = pchoice ] ]
      ask pset [ set label "" ]
      ask my-portal-links [ die ]
      sfx-whoosh
    ]
  ]
end 

to-report make-button [ $type $icon-shape $tooltip $click-action ]
  ;; run by a toolbar
  let this nobody
  let b-x slotx++
  let b-y sloty++

  hatch-controls 1
  [ set this self
    set size button-regular-size
    set heading 0

    set control-tooltip $tooltip
    set control-type $type
    set control-name remove "_icon" $icon-shape

    update-pcolor icon-regular-color
    set label-color black
    set shape "_C:BUTTON"
    UPDATE-base-color icon-regular-color
    show-turtle
    setxy int b-x int b-y
    ask my-linked-patches

    [ set linked-control myself
      m:listen-for e:patch-enter [-> ask linked-control [ do-button-enter ] ]
      m:listen-for e:patch-exit [-> ask linked-control [ do-button-exit ] ]
      m:listen-for e:click [-> ask linked-control [ do-button-click ] ]
      m:listen-for e:click-start [-> ask linked-control [ do-button-down ] ]
      m:listen-for e:click-end [-> ask linked-control [ do-button-up ] ]
    ]
    setxy b-x b-y

    let this-icon nobody

    hatch-icons 1
    [ set heading 0
      set size icon-regular-size
      update-base-color icon-regular-color
      set-shape-safely $icon-shape
      set my-control myself
      set this-icon self
      setxy xcor + .25 * icon-regular-size ycor - .25 * icon-regular-size

    ]
    set my-icon this-icon
    set control-click-action $click-action
  ]
  report this
end 

to update-prop-tooltip
  set control-tooltip (word "THIS " control-name " PLAYS " instrument " (hold button down to change instrument)")
end 

to make-prop-button [ $prototype ]
  ask (make-button "PROP" [name] of $prototype "" [->])
  [ set prop-id [ prop-id ] of $prototype
    set kind [ kind ] of $prototype
    set instrument [ instrument ] of $prototype
    set control-name uppercase [ name ] of $prototype
    ask my-icon [ set shape [ icon-shape ] of $prototype ]
    set my-prototype $prototype
    ask $prototype [ set my-button myself ]
    update-prop-tooltip
    ;; temp move to patch-center
    let xx xcor let yy ycor
    setxy int xx int yy
    ;; figure out linked patches
    ask my-linked-patches
    [ set linked-control myself
      m:listen-for e:long-click-start  [-> ask linked-control [ do-long-press-prop ] ]
      m:listen-for e:long-click-end    [-> ask linked-control [ set color icon-regular-color ] ]
      m:listen-for e:long-click-cancel [-> ask linked-control [ set color icon-regular-color ] ]

    ]
    ;; return to original position
    setxy xx yy

    set control-click-action
    [ ->

      set drawing-mode "PROP"
      ;; de-select old selected prop
      if is-prototype? selected-prototype and selected-prototype != my-prototype
      [ ;; deselect prior selection
        ask selected-prototype
        [ ask my-button
          [ ask my-icon
            [ set size icon-regular-size
              set color base-color
            ]
          ]
        ]
      ]
      set selected-prototype my-prototype
      ask my-icon
      [ set size icon-selected-size
        set color icon-selected-color
      ]

      m:set-shape "m-grab" m:show
    ]
  ]
end 

to do-button-enter
  ;; patch makes linked control run this
  ;; only on button change
  if self != curr-button
  [
    set curr-button self
    ifelse selected-prototype = my-prototype
    or drawing-mode = control-name
    [ ask my-icon [ set size icon-selected-size set color icon-selected-color ] ]
    [ ask my-icon [ set size icon-hover-size set color icon-hover-color ] ]
    send-message control-tooltip
  ]
end 

to do-button-exit
  ;; patch makes linked control run this
  ;; this happens when pointer has left this patch
  ;; only on button change
  ;; if linked-control of m:prior-patch (this patch, this control )
  ;; is not linked-control of m:curr-patch patch about to be entered...
  ;; change curr control
  let new-control nobody
if is-patch? m:curr-patch
  [ set new-control  [ linked-control ] of m:curr-patch ]
  if new-control != self
  [ ;; control change... what kind?
    ;; first...
    ;; un-current this control
    set curr-button nobody

    ifelse is-prototype? selected-prototype and my-prototype = selected-prototype
     or control-name = drawing-mode
    [ ;; return to "selected" appearance
      ask my-icon [ set size icon-selected-size set color icon-selected-color ]
    ]
    [ ;; return to normal apprearance
      ask my-icon [ set size icon-regular-size set color icon-regular-color ]
    ]
    send-message ""
    if not is-control? new-control
    [ ;; no new control
      ;; hide pointer decoration
      m:hide
    ]
  ]
end 

to do-button-click
  m:hide
  m:reset-shape
  run control-click-action
end 

to do-button-down
  m:reset-shape
  ask my-icon [ set size icon-press-size set color icon-press-color]
  if control-type = "PROP"  [  ask my-prototype [ run my-sound-command ] ]
end 

to do-button-up
end 

to-report DRUM-list report
  [
    "ACOUSTIC BASS DRUM"
    "BASS DRUM 1"
    "-"
    "ACOUSTIC SNARE"
    "ELECTRIC SNARE"
    "-"
    "LOW AGOGO"
    "HI AGOGO"
    "LOW BONGO"
    "HI BONGO"
    "LOW CONGA"
    "OPEN HI CONGA"
    "MUTE HI CONGA"
    "HI TIMBALE"
    "LOW TIMBALE"
    "-"
    "LOW TOM"
    "LOW MID TOM"
    "HI MID TOM"
    "HI TOM"
    "LOW FLOOR TOM"
    "HI FLOOR TOM"
    "-"
    "CHINESE CYMBAL"
    "CRASH CYMBAL 1"
    "CRASH CYMBAL 2"
    ;; "RIDE BELL" ;; ride bell causes issues with some midi implementations
    "RIDE CYMBAL 1"
    "RIDE CYMBAL 2"
    "SPLASH CYMBAL"
    "-"
    "CLOSED HI HAT"
    "OPEN HI HAT"
    "PEDAL HI HAT"
    "-"
    "CABASA"
    "CLAVES"
    "COWBELL"
    "HAND CLAP"
    "MARACAS"
    "SIDE STICK"
    "TAMBOURINE"
    "MUTE TRIANGLE"
    "OPEN TRIANGLE"
    "LOW WOOD BLOCK"
    "HI WOOD BLOCK"
    "-"
    "OPEN CUICA"
    "MUTE CUICA"
    "SHORT GUIRO"
    "LONG GUIRO"
    "VIBRASLAP"
    "-"
    "SHORT WHISTLE"
    "LONG WHISTLE"
  ]
end 

to-report instrument-list
  ;; observer> print "[" foreach sound:instruments [ x -> type "  " write x print "" ] print "]"
    report [
    "-- KEYBOARDS"
    "ACOUSTIC GRAND PIANO"
    "BRIGHT ACOUSTIC PIANO"
    "ELECTRIC GRAND PIANO"
    "HONKY-TONK PIANO"
    "ELECTRIC PIANO 1"
    "ELECTRIC PIANO 2"
    "HARPSICHORD"
    "CLAVI"
    "CELESTA"
    "--Struck Keyboards"
    "GLOCKENSPIEL"
    "MUSIC BOX"
    "VIBRAPHONE"
    "MARIMBA"
    "XYLOPHONE"
    "TUBULAR BELLS"
    "DULCIMER"
    "--Organs"
    "DRAWBAR ORGAN"
    "PERCUSSIVE ORGAN"
    "ROCK ORGAN"
    "CHURCH ORGAN"
    "--Blown / Reed Keyboards"
    "REED ORGAN"
    "ACCORDION"
    "HARMONICA"
    "TANGO ACCORDION"
    "--Guitars"
    "NYLON STRING GUITAR"
    "STEEL ACOUSTIC GUITAR"
    "JAZZ ELECTRIC GUITAR"
    "CLEAN ELECTRIC GUITAR"
    "MUTED ELECTRIC GUITAR"
    "OVERDRIVEN GUITAR"
    "DISTORTION GUITAR"
    "GUITAR HARMONICS"
    "--Basses"
    "ACOUSTIC BASS"
    "FINGERED ELECTRIC BASS"
    "PICKED ELECTRIC BASS"
    "FRETLESS BASS"
    "SLAP BASS 1"
    "SLAP BASS 2"
    "SYNTH BASS 1"
    "SYNTH BASS 2"
    "--Strings"
    "VIOLIN"
    "VIOLA"
    "CELLO"
    "CONTRABASS"
    "TREMOLO STRINGS"
    "PIZZICATO STRINGS"
    "ORCHESTRAL HARP"
    "TIMPANI"
    "STRING ENSEMBLE 1"
    "STRING ENSEMBLE 2"
    "SYNTH STRINGS 1"
    "SYNTH STRINGS 2"
    "--Voices"
    "CHOIR AAHS"
    "VOICE OOHS"
    "SYNTH VOICE"
    "--Orchestra Hit"
    "ORCHESTRA HIT"
    "--Brass"
    "TRUMPET"
    "TROMBONE"
    "TUBA"
    "MUTED TRUMPET"
    "FRENCH HORN"
    "BRASS SECTION"
    "SYNTH BRASS 1"
    "SYNTH BRASS 2"
    "SOPRANO SAX"
    "ALTO SAX"
    "TENOR SAX"
    "BARITONE SAX"
    "--Woodwind"
    "OBOE"
    "ENGLISH HORN"
    "BASSOON"
    "CLARINET"
    "--Flute"
    "PICCOLO"
    "FLUTE"
    "RECORDER"
    "PAN FLUTE"
    "BLOWN BOTTLE"
    "SHAKUHACHI"
    "WHISTLE"
    "OCARINA"
    "--Synth Pads"
    "SQUARE WAVE"
    "SAWTOOTH WAVE"
    "CALLIOPE"
    "CHIFF"
    "CHARANG"
    "VOICE"
    "FIFTHS"
    "BASS AND LEAD"
    "NEW AGE"
    "WARM"
    "POLYSYNTH"
    "CHOIR"
    "BOWED"
    "METAL"
    "HALO"
    "SWEEP"
    "RAIN"
    "SOUNDTRACK"
    "CRYSTAL"
    "ATMOSPHERE"
    "BRIGHTNESS"
    "GOBLINS"
    "ECHOES"
    "SCI-FI"
    "--World Instr"
    "SITAR"
    "BANJO"
    "SHAMISEN"
    "KOTO"
    "KALIMBA"
    "BAG PIPE"
    "FIDDLE"
    "SHANAI"
    "TINKLE BELL"
    "--Percussion"
    "AGOGO"
    "STEEL DRUMS"
    "WOODBLOCK"
    "TAIKO DRUM"
    "MELODIC TOM"
    "SYNTH DRUM"
    "REVERSE CYMBAL"
    "--SFX"
    "GUITAR FRET NOISE"
    "BREATH NOISE"
    "SEASHORE"
    "BIRD TWEET"
    "TELEPHONE RING"
    "HELICOPTER"
    "APPLAUSE"
    "GUNSHOT"
  ]
end 

to do-click-start
  ( ifelse
    ( drawing-mode = "PROP" ) [ set erase? any? props-here ]
    ( drawing-mode = "ROAD" ) [ set erase? any? roads-here ]
   [ set erase? false ]
  )
end 

to do-click
  draw-thing e:click
end 

to do-drag-start
  ( ifelse
    ( drawing-mode = "ROAD" or drawing-mode = "ERASE" )
    [ draw-line-of-road-drag-start ]
    ( drawing-mode = "PROP" )
    [ let dp [ props-here with [ kind = "NOTE" ] ] of m:drag-patch
      if any? dp
      [ set dp one-of dp
        let dpx [ pxcor ] of m:drag-patch
        let y-diff [ pycor ] of m:curr-patch - [ pycor] of m:drag-patch
        let dpnote [ note ] of dp
        let temp-note y-diff + dpnote
        if temp-note > 127 [ set temp-note 127 ]
        let note-name (word (item (temp-note mod 12) note-labels) "-" int (temp-note / 12 - 1) )
        ask dp
        [ play-sample temp-note
          ask neighbors ;; apply note labels
          [
          ;  set plabel note-name
          ;  set pcolor white
          ;  set plabel-color black
            ask turtles-here with [ breed != m:pointers ] [ hide-turtle ]
          ]
        ]
        ask field-patches with [ pxcor = dpx ]
        [ set temp-note dpnote + pycor - [ pycor ] of m:drag-patch
          set plabel (word (item (temp-note mod 12) note-labels) "-" int (temp-note / 12 - 1) )
          ask patches at-points [[ -1 0 ] [ 0 0  ] [ 1 0] ]
          [ set pcolor white
            set plabel-color black
          ]
        ]
      ]
    ]
    []
  )
end 

to do-drag-enter
  ( ifelse
    (drawing-mode = "ROAD" or drawing-mode = "ERASE") [ draw-line-of-road-drag-enter]
    (drawing-mode = "PROP") [
      if any? [ props-here with [ kind = "NOTE" ]] of m:drag-patch
      [
        ask m:drag-patch
        [ let ny [ pycor ] of m:curr-patch - [ pycor] of m:down-patch
          ask props-here
          [
            play-sample (note + ny)
          ]
          ask patch pxcor [ pycor ] of m:curr-patch [ ask patches at-points [ [ -1 0][0 0][1 0]] [ set pcolor gray + 2]]
        ]

      ]
    ]
    []
  )
end 

to do-drag-exit
  ( ifelse
    (drawing-mode = "PROP")
    [
      if any? [ props-here with [ kind = "NOTE" ]] of m:drag-patch
      [
        ask m:drag-patch
        [
        ask patch pxcor [ pycor ] of m:prior-patch [ ask patches at-points [ [ -1 0][0 0][1 0]] [ set pcolor white]]
        ]
      ]
    ]
    []
  )
end 

to do-drag-drop
  ( ifelse
    ( drawing-mode = "ROAD" or drawing-mode = "ERASE") [ draw-line-of-road-drag-drop ]
    ( drawing-mode = "PROP" )
    [ ask m:drag-patch
      [ if any? [ props-here with [ kind = "NOTE" ] ] of m:drag-patch
        [ let dp [ props-here with [ kind = "NOTE" ] ] of m:drag-patch
          if any? dp
          [ set dp one-of dp
            let dpx [ pxcor ] of m:drag-patch
            let y-diff [ pycor ] of m:curr-patch - [ pycor] of m:drag-patch
            let dpnote [ note ] of dp
            let temp-note y-diff + dpnote
            if temp-note > 127 [ set temp-note 127 ]
            let note-name (word (item (temp-note mod 12) note-labels) "-" int (temp-note / 12 - 1) )
            ask dp
            [ ;; play-sample temp-note
              ask neighbors ;; apply note labels
              [ set plabel ""
                ask turtles-here with [ breed != m:pointers ] [ show-turtle ]
                set pcolor base-pcolor
              ]
              set note temp-note
              set duration .25 * (1 + abs ( pxcor - [ pxcor ] of m:drop-patch ) )
            ]
            ask field-patches with [ pxcor = dpx ]
            [ ask patches at-points [[ -1 0 ] [ 0 0  ] [ 1 0] ]
             [  set plabel ""
              set pcolor base-pcolor
              set plabel-color white
            ]]
          ]
        ]
      ]
    ]
    []
  )
end 

to do-drag-cancel
  ask m:Mouse [ m:do-event e:drag-exit m:curr-patch set dragging? false]
  if is-patch-set? dragset [ ask dragset [set pcolor base-pcolor ] ]
  ask markers [ die ]
end 

to draw-thing [ event ]
  (ifelse
    (drawing-mode = "ROAD") [ ifelse erase? [ erase-things] [ make-road-here event ] ]
    (drawing-mode = "PROP") [ sfx-click make-prop-here selected-prototype event  ]
    (drawing-mode = "CAR" ) [ make-car-here event ]
    (drawing-mode = "ERASE" ) [ erase-things ]
    (drawing-mode = "PORTAL" )  [  make-portal-here ]
    (drawing-mode = "ONEWAY" ) [ make-one-way-here ]
  )
end 

to draw-line-of-road-drag-start
  ;; run by ddrag-patch aka prior-patch
  let dxa abs ( [ pxcor ] of m:curr-patch  - pxcor )
  let dya abs ( [ pycor ] of m:curr-patch  - pycor )
  ifelse dxa != 0 and dya != 0
  [ ask m:mouse [ m:do-event e:drag-cancel drag-patch ]  ]
  [
    sprout-markers 1
    [ update-base-color lime
      set my-snap myself
      set drag-axis-x 1
      set drag-axis-y 1
      set dragroot self
    ]
    sprout-markers 1
    [ update-base-color lime
      set shape "m-control-decorator-target"
      move-to [ curr-patch ] of m:mouse
      set drag-axis-y dxa
      set drag-axis-x dya

      set my-snap m:mouse
      set dragleaf self
      create-marker-link-from dragroot
    ]
  ]
end 

to draw-line-of-road-drag-enter
  if is-marker? dragleaf and is-marker? dragroot
  [ ask dragleaf
    [ let px [ pxcor ] of my-snap
      let py [ pycor ] of my-snap
      let rx [ pxcor ] of dragleaf
      let ry [ pycor ] of dragleaf
      let nx (px * drag-axis-y ) + (rx * drag-axis-x)
      let ny (py * drag-axis-x ) + (ry * drag-axis-y)
      if is-patch? patch nx ny
      [ setxy nx ny ]

    ]
    let p no-patches
    ask dragroot
    [ let d distance dragleaf
      if d > 0
      [ let h towards dragleaf
        set heading h
        set p patches in-cone d .1
      ]
    ]
    if is-patch-set? dragset [ ask dragset [ set pcolor base-pcolor ] ]
    set dragset p
    ask dragset
    [ set pcolor ifelse-value ( ( int (distance dragroot) ) mod beats-per-measure) = 0 [ red ] [green ] ]
    let n int ( ( (count dragset ) - 1 ) / beats-per-measure )
    sound:play-note "clavi" (n * 5 + 32) velocity / 2 .05
    ;;; sound:play-note instrument keynumber velocity duration
  ]
end 

to-report  is-grid-intersection?
  report  ifelse-value ( beats-per-measure = 0 ) [ false ] [ (( pxcor mod beats-per-measure) = 0 ) and ( (pycor mod beats-per-measure)  = 0) ]
end 

to draw-line-of-road-drag-drop
  let temp nobody
  if is-patch-set? dragset
  [ ask dragset [ set pcolor base-pcolor ]
    ifelse (drawing-mode = "ERASE")
    [ ask dragset [ erase-things-partial ]
      ask dragset [ redraw-neighbor-roads ]
    ]
    [ ask dragset [ make-road-here e:null-event ] ]

  ]
  ask markers [ die ]
end 

to erase-things
  let also-roads? any? roads-here
  ask props-here [ die ]
  ask cars-here [ die ]
  erase-portals-here
  ask roadsigns-here [ die ]
  if also-roads?
  [ ask roads-here [ die ]
    ask (props-on neighbors4 ) with [ not any? roads-on neighbors4 ] [ die ]
    redraw-neighbor-roads
  ]
  set-field-color
end 

to erase-things-partial
  let also-roads? any? roads-here
  ask props-here [ die ]
  ask cars-here [ die ]
  erase-portals-here
  ask roadsigns-here [ die ]
  if also-roads?
  [ ask roads-here [ die ]
    ask (props-on neighbors4 ) with [ not any? roads-on neighbors4 ] [ die ]
  ]
  set-field-color
end 

to erase-portals-here
  ask portals-here
  [ ask portals with [ destination = myself ]
    [ set destination nobody ]
    die
  ]
end 

to update-mouse-label
   ask m:mouse
    [ let t drawing-mode
      if drawing-mode = "PROP" [ if is-turtle? selected-prototype [ set t [name] of selected-prototype ] ]
      set label (word "\n\n\n" t "              ")
    ]
end 

to _edit
  every .01
  [ m:go-mouse
    update-mouse-label
  ]
  every .1
  [ ask portals [ rt 5 if color mod 10 < 5 or color mod 10 > 8 [ set pulse -1 * pulse ] set color color + pulse  ]
    ask portal-links [ set color [ color] of end1 set expires expires - 1 if expires <= 0 [ die ] ]
  ]
end 

to _play
  every ( 15 / tempo ) [ if is-playing? [ _drive ] ]
end 

to _drive
  ; if ( playing? = true )
  ; [
  ask cars
  [
    let this-car self
    set sight-patch patch-here
    set sight-heading heading
    let choices no-turtles
    let choice nobody
    let this-sign nobody
    ;let signs-near (roadsigns-on neighbors4 ) with [ kind = "one-way" ]
    ;ifelse any? signs-near
    ;[ set this-sign one-of signs-near
    ;  set choices roads-on (patch-set
    ;    patch-at-heading-and-distance [ heading ] of this-sign 1
    ;  )
    ;]
    ;[
    let this-patch sight-patch
      set choices roads-on ((patch-set
      patch-ahead 1
      patch-at-heading-and-distance (90 + heading) 1
      patch-at-heading-and-distance (-90 + heading) 1
    ) with [ not any? (roadsigns-here with [ kind = "one-way" and 0 = subtract-headings heading [180 + towards myself] of this-patch ] ) ] )
    ;]
    ifelse (any? choices)
    [ set choice one-of choices
      set sight-patch [ patch-here ] of choice
      set sight-heading towards choice
    ] ;; jump 1
    [ ifelse ( any? roads-on patch-ahead -1 )
      [ set sight-patch patch-ahead -1
        set sight-heading heading + 180
      ]
      [ set sight-heading heading + 90
      ]
    ]

    ;; jump 1
    ;; handle portals
    ;; if there's a portal, look at the destination
    let ppp portals-on sight-patch
    if any? ppp with [ is-portal? destination ] [ set sight-patch [ destination ] of one-of ppp ask ppp [ create-portal-link-to destination [ set shape "portal-link-to" set color white set expires 2 ]] ]

    set sights [ props-on neighbors4 ] of sight-patch
  ]
  if is-turtle-set? all-sights [ ask all-sights [ set color base-color ] ]
  set all-sights (turtle-set [ sights ] of cars )
  ask all-sights [ play-my-sound ]
  ask cars [ set heading sight-heading set shape (word "_car_3_" heading ) move-to sight-patch ]
  ask all-sights [ set color prop-hit-color ]
end 

to __setup
  clear-all
  send-message "GETTING READY FOR FUN!"

  output-print "calling mouse setup"
  m:setup-mouse-driver
  ask m:mouse [ set shape "m-pointer-1" ]


  set tempo 120
  set velocity 64
  set beats-per-measure 4
  set last-note 60 ;; middle c
  setup-prototypes

  _setup-toolbar

  _setup-field-patches

  ask patches with [ (pxcor = min-pxcor or pxcor = max-pxcor) and (pycor = min-pycor or pycor = max-pycor) ]
  [ m:listen-for e:screen-exit [ -> do-drag-cancel ] ]

  set note-labels [ "C" "C#" "D" "D#" "E" "F" "F#" "G" "G#" "A" "A#" "B" ]
  output-print ""

  set drawing-mode "ROAD"

  set is-playing? false
  reset-ticks
  output-print "all done!"
  send-message "WELCOME TO TOON-TOWN! -- PRESS PLAY TO BEGIN"
end 

There are 6 versions of this model.

Uploaded by When Description Download
James Steiner about 1 month ago Melodic buttons, drag to change pitch/duration Download this version
James Steiner 2 months ago Removed embedded mose driver code Download this version
James Steiner 2 months ago Now with Portals and One-Way signs, more props Download this version
James Steiner 2 months ago tweaks, bug-fixes, shape edits, new shapes Download this version
James Steiner 2 months ago drumset is loaded/saved from disk. bug-fixes, can change drums Download this version
James Steiner 2 months ago Initial upload Download this version

Attached files

File Type Description Last updated
mouse-driver-2022-2.nls extension latest bug fixes 2 months ago, by James Steiner Download
Tune-Town 2.png preview new new new preview 2 months ago, by James Steiner Download

This model does not have any ancestors.

This model does not have any descendants.