├── .gitignore ├── LICENSE ├── README.md ├── autoload └── killersheep.vim └── plugin ├── beh.mp3 ├── beh.ogg ├── fail.mp3 ├── fail.ogg ├── fire.mp3 ├── fire.ogg ├── killersheep.vim ├── music.mp3 ├── music.ogg ├── poop.mp3 ├── poop.ogg ├── quack.mp3 ├── quack.ogg ├── win.mp3 └── win.ogg /.gitignore: -------------------------------------------------------------------------------- 1 | # All platforms 2 | *.rej 3 | *.orig 4 | *.mo 5 | *.swp 6 | *~ 7 | 8 | # Mac OSX 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Killersheep game goes under the same license as Vim. 2 | You can find the text here: 3 | https://github.com/vim/vim/blob/master/runtime/doc/uganda.txt 4 | 5 | It basically means you can copy the files as you like and also re-distribute 6 | them, so long as you keep the license. 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # killersheep 2 | 3 | Silly game to show off the new features of Vim 8.2: 4 | - Popup windows with colors and mask 5 | - Text properties to highlight text 6 | - Sound 7 | 8 | Installation 9 | ------------ 10 | 11 | Use your favorite plugin manager. 12 | 13 | For example, using [vim-plug](https://github.com/junegunn/vim-plug): 14 | 15 | ```vim 16 | Plug 'vim/killersheep' 17 | ``` 18 | 19 | Or download the files using the zip archive, and unpack them in your 20 | pack directory `~/.vim/pack/mine/opt/killersheep/`. 21 | Then load the pack manually with: 22 | 23 | ```vim 24 | packadd killersheep 25 | ``` 26 | 27 | Or put this in your vimrc: 28 | 29 | ```vim 30 | packadd! killersheep 31 | ``` 32 | 33 | How to play 34 | ----------- 35 | 36 | First of all: make sure you can hear the sound (or put on your headphones if 37 | you don't want your friends/colleagues to find out what you are doing). 38 | 39 | ```vim 40 | :KillKillKill 41 | ``` 42 | 43 | Or, if you don't have conflicting commands, just: 44 | 45 | ```vim 46 | :Kill 47 | ``` 48 | 49 | In the game: 50 | 51 | | Key | Description | 52 | | ------- | ----------------- | 53 | | l | move cannon right | 54 | | h | move cannon left | 55 | | Space | fire cannon | 56 | | Esc | exit game | 57 | 58 | 59 | Requirements 60 | ------------ 61 | 62 | - Vim 8.2 63 | - feature +textprop 64 | - feature +sound or command "afplay", "paplay" or "cvlc". 65 | - terminal with at least 45 lines 66 | -------------------------------------------------------------------------------- /autoload/killersheep.vim: -------------------------------------------------------------------------------- 1 | " Implementation of the silly game 2 | " Use :KillKillKill to start 3 | " 4 | " Last Update: 2019 Dec 7 5 | 6 | let s:did_init = 0 7 | let s:sound_cmd = '' 8 | 9 | func killersheep#Start(sounddir) 10 | let s:dir = a:sounddir 11 | 12 | if !has('sound') 13 | if executable('afplay') 14 | " Probably on Mac 15 | let s:sound_cmd = 'afplay' 16 | let g:killersheep_sound_ext = '.mp3' 17 | elseif executable('paplay') 18 | " Probably on Unix 19 | let s:sound_cmd = 'paplay' 20 | let g:killersheep_sound_ext = '.ogg' 21 | elseif executable('cvlc') 22 | " Probably on Unix 23 | let s:sound_cmd = 'cvlc --play-and-exit' 24 | let g:killersheep_sound_ext = '.ogg' 25 | else 26 | echomsg 'This build of Vim is lacking sound support, you are missing out!' 27 | sleep 2 28 | endif 29 | endif 30 | 31 | if !s:did_init 32 | let s:did_init = 1 33 | call s:Init() 34 | endif 35 | 36 | call s:Clear() 37 | call s:Intro() 38 | endfunc 39 | 40 | func s:NoProp(text) 41 | return #{text: a:text, props: []} 42 | endfunc 43 | 44 | func s:Intro() 45 | hi SheepTitle cterm=bold gui=bold 46 | hi introHL ctermbg=cyan guibg=cyan 47 | call prop_type_delete('sheepTitle') 48 | call prop_type_add('sheepTitle', #{highlight: 'SheepTitle'}) 49 | call prop_type_delete('introHL') 50 | call prop_type_add('introHL', #{highlight: 'introHL'}) 51 | let s:intro = popup_create([ 52 | \ #{text: ' The sheep are out to get you!', 53 | \ props: [#{col: 4, length: 29, type: 'sheepTitle'}]}, 54 | \ s:NoProp(''), 55 | \ s:NoProp('In the game:'), 56 | \ #{text: ' h move cannon left', 57 | \ props: [#{col: 6, length: 1, type: 'sheepTitle'}]}, 58 | \ #{text: ' l move cannon right', 59 | \ props: [#{col: 6, length: 1, type: 'sheepTitle'}]}, 60 | \ #{text: ' fire', 61 | \ props: [#{col: 3, length: 7, type: 'sheepTitle'}]}, 62 | \ #{text: ' quit (colon also works)', 63 | \ props: [#{col: 4, length: 5, type: 'sheepTitle'}]}, 64 | \ s:NoProp(''), 65 | \ #{text: 'Now press s to start or x to exit', 66 | \ props: [#{col: 12, length: 1, type: 'sheepTitle'}, 67 | \ #{col: 28, length: 1, type: 'sheepTitle'}]}, 68 | \ ], #{ 69 | \ filter: function('s:IntroFilter'), 70 | \ callback: function('s:IntroClose'), 71 | \ border: [], 72 | \ padding: [], 73 | \ mapping: 0, 74 | \ drag: 1, 75 | \ close: 'button', 76 | \ }) 77 | if has('sound') || len(s:sound_cmd) 78 | let s:keep_playing = 1 79 | call s:PlayMusic() 80 | endif 81 | call s:IntroHighlight(0) 82 | endfunc 83 | 84 | const s:introHL = [[4, 3], [8, 5], [14, 3], [18, 3], [22, 2], [25, 3], [29, 4]] 85 | let s:intro_timer = 0 86 | func s:IntroHighlight(idx) 87 | let idx = a:idx 88 | if idx >= len(s:introHL) 89 | let idx = 0 90 | endif 91 | let buf = winbufnr(s:intro) 92 | call prop_remove(#{type: 'introHL', bufnr: buf}, 1) 93 | call prop_add(1, s:introHL[idx][0], 94 | \ #{length: s:introHL[idx][1], bufnr: buf, type: 'introHL'}) 95 | let s:intro_timer = timer_start(300, { -> s:IntroHighlight(idx + 1)}) 96 | endfunc 97 | 98 | func s:IntroFilter(id, key) 99 | if a:key == 's' || a:key == 'S' 100 | call s:Clear() 101 | let s:round = 0 102 | let s:ready = popup_create('Get Ready!', #{border: [], padding: [2, 4, 2, 4]}) 103 | call s:BlinkLevel(s:ready, 1) 104 | call timer_start(s:blink_time * 8, { -> s:NextRound()}) 105 | let s:ready_timer = timer_start(300, {... -> s:ReadySound()}) 106 | elseif a:key == 'x' || a:key == 'X' || a:key == "\" 107 | call s:Clear() 108 | endif 109 | return 1 110 | endfunc 111 | 112 | func s:ReadySound() 113 | call s:PlaySound('quack') 114 | let s:ready_timer = timer_start(s:blink_time * 2, {... -> s:ReadySound()}) 115 | endfunc 116 | 117 | func s:IntroClose(id, res) 118 | call s:Clear() 119 | endfunc 120 | 121 | " Play the music in a loop 122 | func s:PlayMusic() 123 | if s:keep_playing 124 | let fname = s:dir .. '/music' .. g:killersheep_sound_ext 125 | if has('sound') 126 | let s:music_id = sound_playfile(fname, {x -> s:PlayMusic()}) 127 | elseif len(s:sound_cmd) 128 | let s:music_job = job_start(s:sound_cmd .. ' ' .. fname) 129 | " Detecting job exit is a bit slow, use a timer to loop. 130 | let s:music_timer = timer_start(14100, {x -> s:PlayMusic()}) 131 | endif 132 | endif 133 | endfunc 134 | 135 | func s:StopMusic() 136 | let s:keep_playing = 0 137 | if has('sound') 138 | call sound_clear() 139 | elseif len(s:sound_cmd) && exists('s:music_job') 140 | call job_stop(s:music_job) 141 | call timer_stop(s:music_timer) 142 | unlet s:music_job s:music_timer 143 | endif 144 | endfunc 145 | 146 | func s:Init() 147 | hi def KillerCannon ctermbg=blue guibg=blue 148 | hi def KillerBullet ctermbg=red guibg=red 149 | hi def KillerSheep ctermfg=black ctermbg=green guifg=black guibg=green 150 | hi def KillerSheep2 ctermfg=black ctermbg=cyan guifg=black guibg=cyan 151 | if &bg == 'light' 152 | hi def KillerPoop ctermbg=black guibg=black 153 | else 154 | hi def KillerPoop ctermbg=white guibg=white 155 | endif 156 | hi def KillerPoop ctermbg=black guibg=black 157 | hi def KillerLevel ctermbg=magenta guibg=magenta 158 | hi def KillerLevelX ctermbg=yellow guibg=yellow 159 | 160 | if !exists('g:killersheep_sound_ext') 161 | if has('win32') || has('osx') || len(s:sound_cmd) 162 | " most systems can play MP3 files 163 | let g:killersheep_sound_ext = ".mp3" 164 | else 165 | " libcanberra defaults to supporting ogg only 166 | let g:killersheep_sound_ext = ".ogg" 167 | endif 168 | endif 169 | endfunc 170 | 171 | func s:NextRound() 172 | call s:Clear() 173 | 174 | let s:round += 1 175 | let s:sheepcount = 0 176 | let s:frozen = 0 177 | call s:ShowBulletSoon() 178 | 179 | " Every once in a while let the next sheep that moves poop. 180 | let s:wantpoop = 0 181 | let s:poop_timer = timer_start(s:poop_interval[s:round - 1], {x -> s:WantPoop()}, #{repeat: -1}) 182 | 183 | " Create a few sheep to kill. 184 | let topline = &lines > 50 ? &lines - 50 : 0 185 | call s:PlaceSheep(topline + 0, 5, 'KillerSheep') 186 | call s:PlaceSheep(topline + 5, 75, 'KillerSheep2') 187 | call s:PlaceSheep(topline + 7, 35, 'KillerSheep') 188 | call s:PlaceSheep(topline + 10, 15, 'KillerSheep') 189 | call s:PlaceSheep(topline + 12, 70, 'KillerSheep') 190 | call s:PlaceSheep(topline + 15, 55, 'KillerSheep2') 191 | call s:PlaceSheep(topline + 20, 15, 'KillerSheep2') 192 | call s:PlaceSheep(topline + 21, 30, 'KillerSheep') 193 | call s:PlaceSheep(topline + 22, 60, 'KillerSheep2') 194 | call s:PlaceSheep(topline + 28, 0, 'KillerSheep') 195 | call s:ShowLevel(topline) 196 | 197 | let s:canon_id = popup_create([' /#\ ', ' /###\ ', '/#####\'], #{ 198 | \ line: &lines - 2, 199 | \ highlight: 'KillerCannon', 200 | \ filter: function('s:MoveCanon'), 201 | \ zindex: s:cannon_zindex, 202 | \ mask: [[1,2,1,1], [6,7,1,1], [1,1,2,2], [7,7,2,2]], 203 | \ mapping: 0, 204 | \ }) 205 | endfunc 206 | 207 | func s:ShowLevel(line) 208 | let s:levelid = popup_create('Level ' .. s:round, #{ 209 | \ line: a:line ? a:line : 1, 210 | \ border: [], 211 | \ padding: [0,1,0,1], 212 | \ highlight: 'KillerLevel'}) 213 | endfunc 214 | 215 | func s:MoveCanon(id, key) 216 | if s:frozen 217 | return 218 | endif 219 | let pos = popup_getpos(a:id) 220 | 221 | let move = 0 222 | if a:key == 'h' && pos.col > 1 223 | let move = pos.col - 2 224 | endif 225 | if a:key == 'l' && pos.col < &columns - 6 226 | let move = pos.col + 2 227 | endif 228 | if move != 0 229 | call popup_move(a:id, #{col: move}) 230 | if s:bullet_available 231 | call popup_move(s:bullet_available, #{col: move + 3}) 232 | endif 233 | endif 234 | 235 | if a:key == ' ' 236 | call s:Fire(pos.col + 3) 237 | endif 238 | if a:key == "\e" || a:key == 'x' || a:key == ':' 239 | call s:Clear() 240 | endif 241 | return a:key != ':' 242 | endfunc 243 | 244 | const s:bullet_holdoff = 800 245 | const s:bullet_delay = 30 246 | const s:poop_delay = 60 247 | const s:sheep_anim = 40 248 | const s:sheep_explode = 150 249 | const s:cannon_zindex = 100 250 | const s:bullet_zindex = 80 251 | const s:sheep_zindex = 90 252 | const s:poop_interval = [700, 500, 300, 200, 100] 253 | const s:poop_countdown = 300 / s:sheep_anim 254 | const s:blink_time = 300 255 | 256 | const s:sheepSprite = [[ 257 | \ ' o^^) /^^^^^^\ ', 258 | \ '==___ |', 259 | \ ' \ ___ _/', 260 | \ ' || || '],[ 261 | \ ' o^^) /^^^^^^\ ', 262 | \ '==___ |', 263 | \ ' \_ ____ _/', 264 | \ ' | | '],[ 265 | \ ' o^^) /^^^^^^\ ', 266 | \ '==___ |', 267 | \ ' \ ___ _/', 268 | \ ' || || '],[ 269 | \ ' o^^) /^^^^^^\ ', 270 | \ '==___ |', 271 | \ ' \ _ __ _ /', 272 | \ ' / | / | '],[ 273 | \ ' /^^^^^^\ ', 274 | \ ' | |', 275 | \ ' O^^) ', 276 | \ 'xx___ _ |', 277 | \ ' \ _____ _/', 278 | \ ' || || '],[ 279 | \ ' /^^^^^^\ ', 280 | \ ' | |', 281 | \ ' ', 282 | \ ' O^^) ', 283 | \ 'XX___ ', 284 | \ ' \ __ _ _/', 285 | \ ' || || ']] 286 | const s:sheepSpriteMask = [[ 287 | \ ' xxxx xxxxxxxx ', 288 | \ 'xxxxxxxxxxxxxxx', 289 | \ ' xxxxxxxxxx', 290 | \ ' xx xx '],[ 291 | \ ' xxxx xxxxxxxx ', 292 | \ 'xxxxxxxxxxxxxxx', 293 | \ ' xxxxxxxxxx', 294 | \ ' x x '],[ 295 | \ ' xxxx xxxxxxxx ', 296 | \ 'xxxxxxxxxxxxxxx', 297 | \ ' xxxxxxxxxx', 298 | \ ' xx xx '],[ 299 | \ ' xxxx xxxxxxxx ', 300 | \ 'xxxxxxxxxxxxxxx', 301 | \ ' xxxxxxxxxx', 302 | \ ' x x x x '],[ 303 | \ ' xxxxxxxx ', 304 | \ ' xxxxxxxxxx', 305 | \ ' xxxx ', 306 | \ 'xxxxx xxxxxxxxxxx', 307 | \ ' xxxxxxxxxxx', 308 | \ ' xx xx '],[ 309 | \ ' xxxxxxxx ', 310 | \ ' xxxxxxxxxx', 311 | \ ' ', 312 | \ ' xxxx ', 313 | \ 'xxxxx ', 314 | \ ' xxxxxxxxxxx', 315 | \ ' xx xx ']] 316 | 317 | func GetMask(l) 318 | let mask = [] 319 | for r in range(len(a:l)) 320 | let s = 0 321 | let e = -1 322 | let l = a:l[r] 323 | for c in range(len(l)) 324 | if l[c] == ' ' 325 | let e = c 326 | elseif e >= s 327 | call add(mask, [s+1,e+1,r+1,r+1]) 328 | let s = c + 1 329 | let e = c 330 | else 331 | let s = c + 1 332 | endif 333 | endfor 334 | if e >= s 335 | call add(mask, [s+1,e+1,r+1,r+1]) 336 | endif 337 | endfor 338 | return mask 339 | endfunc 340 | 341 | let s:sheepMasks = [] 342 | for l in s:sheepSpriteMask 343 | call add(s:sheepMasks, GetMask(l)) 344 | endfor 345 | 346 | func s:PlaceSheep(line, col, hl) 347 | let id = popup_create(s:sheepSprite[0], #{ 348 | \ line: a:line, 349 | \ col: a:col, 350 | \ highlight: a:hl, 351 | \ mask: s:sheepMasks[0], 352 | \ fixed: 1, 353 | \ zindex: s:sheep_zindex, 354 | \ wrap: 0, 355 | \}) 356 | call setwinvar(id, 'left', 0) 357 | call setwinvar(id, 'dead', 0) 358 | call timer_start(s:sheep_anim, {x -> s:AnimSheep(id, 1)}) 359 | let s:sheepcount += 1 360 | sleep 10m 361 | return id 362 | endfunc 363 | 364 | func s:AnimSheep(id, state) 365 | if s:frozen 366 | return 367 | endif 368 | let pos = popup_getpos(a:id) 369 | if pos == {} 370 | return 371 | endif 372 | if getwinvar(a:id, 'dead') 373 | return 374 | endif 375 | let left = getwinvar(a:id, 'left') 376 | if left == 1 377 | if pos.line > &lines - 11 378 | call s:PlaySoundForEnd() 379 | endif 380 | call popup_setoptions(a:id, #{pos: 'topleft', col: &columns + 1, line: pos.line + 5}) 381 | let left = 0 382 | elseif pos.col > 1 383 | call popup_move(a:id, #{col: pos.col - 1}) 384 | else 385 | if left == 0 386 | let left = 15 387 | endif 388 | call popup_setoptions(a:id, #{pos: 'topright', col: left - 1}) 389 | let left -= 1 390 | endif 391 | let poopid = getwinvar(a:id, 'poopid') 392 | if poopid 393 | let poopcount = getwinvar(a:id, 'poopcount') 394 | if poopcount == 1 395 | " drop the poop 396 | call popup_close(poopid) 397 | call setwinvar(a:id, 'poopid', 0) 398 | call s:Poop(pos.line + 1, left ? left : pos.col + 12) 399 | else 400 | call popup_move(poopid, #{col: left ? left + 1 : pos.col + 14, 401 | \ line: pos.line + 1}) 402 | endif 403 | call setwinvar(a:id, 'poopcount', poopcount - 1) 404 | endif 405 | 406 | call setwinvar(a:id, 'left', left) 407 | call popup_settext(a:id, s:sheepSprite[a:state]) 408 | call popup_setoptions(a:id, #{mask: s:sheepMasks[a:state]}) 409 | call timer_start(s:sheep_anim, {x -> s:AnimSheep(a:id, a:state == 3 ? 0 : a:state + 1)}) 410 | 411 | if left || pos.col < &columns - 14 412 | if s:wantpoop && !getwinvar(a:id, 'poopid') 413 | let s:wantpoop = 0 414 | call setwinvar(a:id, 'poopcount', s:poop_countdown) 415 | let poopid = popup_create('x', #{ 416 | \ col: left ? left + 1 : pos.col + 14, 417 | \ line: pos.line + 1, 418 | \ highlight: 'KillerPoop', 419 | \ zindex: s:bullet_zindex, 420 | \ }) 421 | call setwinvar(a:id, 'poopid', poopid) 422 | endif 423 | endif 424 | endfunc 425 | 426 | func s:KillSheep(id, state) 427 | let pos = popup_getpos(a:id) 428 | if pos == {} 429 | return 430 | endif 431 | let poopid = getwinvar(a:id, 'poopid') 432 | if poopid 433 | call popup_close(poopid) 434 | endif 435 | let left = getwinvar(a:id, 'left') 436 | if a:state == 6 437 | let s:sheepcount -= 1 438 | if s:sheepcount == 0 439 | call s:PlaySoundForEnd() 440 | endif 441 | call popup_close(a:id) 442 | return 443 | endif 444 | call popup_settext(a:id, s:sheepSprite[a:state]) 445 | call popup_setoptions(a:id, #{mask: s:sheepMasks[a:state], line: pos.line - 1, col: pos.col - 1}) 446 | call timer_start(s:sheep_explode, {x -> s:KillSheep(a:id, a:state + 1)}) 447 | call setwinvar(a:id, 'dead', 1) 448 | endfunc 449 | 450 | func s:WantPoop() 451 | let s:wantpoop = 1 452 | endfunc 453 | 454 | func s:Poop(line, col) 455 | if s:frozen 456 | return 457 | endif 458 | let id = popup_create(['|', '|'], #{ 459 | \ col: a:col, 460 | \ line: a:line, 461 | \ highlight: 'KillerPoop', 462 | \ zindex: s:bullet_zindex, 463 | \ }) 464 | call s:PlaySound('poop') 465 | call timer_start(s:poop_delay, {x -> s:MovePoop(x, id)}, #{repeat: -1}) 466 | endfunc 467 | 468 | func s:MovePoop(x, id) 469 | if s:frozen 470 | return 471 | endif 472 | let pos = popup_getpos(a:id) 473 | if pos == {} 474 | call timer_stop(a:x) 475 | return 476 | endif 477 | if pos.line >= &lines - 1 478 | call popup_close(a:id) 479 | call timer_stop(a:x) 480 | else 481 | call popup_move(a:id, #{line: pos.line + 2}) 482 | let winid = popup_locate(pos.line + 2, pos.col) 483 | " TODO: no hit if no overlap 484 | if winid != 0 && winid == s:canon_id 485 | call s:PlaySoundForEnd() 486 | endif 487 | endif 488 | endfunc 489 | 490 | func s:ShowBulletSoon() 491 | let s:bullet_available = 0 492 | let s:bullet_timer = timer_start(s:bullet_holdoff, {x -> ShowBullet()}) 493 | endfunc 494 | 495 | func ShowBullet() 496 | if s:frozen 497 | return 498 | endif 499 | let s:bullet_timer = 0 500 | let pos = popup_getpos(s:canon_id) 501 | let s:bullet_available = popup_create(['|', '|'], #{ 502 | \ col: pos.col + 3, 503 | \ line: &lines - 3, 504 | \ highlight: 'KillerBullet', 505 | \ zindex: s:bullet_zindex, 506 | \ }) 507 | endfunc 508 | 509 | func s:Fire(col) 510 | if s:frozen 511 | return 512 | endif 513 | if !s:bullet_available 514 | return 515 | endif 516 | let id = s:bullet_available 517 | call s:ShowBulletSoon() 518 | 519 | call s:PlaySound('fire') 520 | call timer_start(s:bullet_delay, {x -> s:MoveBullet(x, id)}, #{repeat: -1}) 521 | endfunc 522 | 523 | func s:MoveBullet(x, id) 524 | if s:frozen 525 | return 526 | endif 527 | let pos = popup_getpos(a:id) 528 | if pos == {} 529 | call timer_stop(a:x) 530 | return 531 | endif 532 | if pos.line <= 2 533 | call popup_close(a:id) 534 | call timer_stop(a:x) 535 | else 536 | call popup_move(a:id, #{line: pos.line - 2}) 537 | let winid = popup_locate(pos.line - 2, pos.col) 538 | if winid != 0 && winid != a:id 539 | call s:KillSheep(winid, 4) 540 | if s:sheepcount == 1 541 | let s:frozen = 1 542 | endif 543 | call s:PlaySound('beh') 544 | call popup_close(a:id) 545 | endif 546 | endif 547 | endfunc 548 | 549 | func s:PlaySound(name) 550 | let fname = s:dir .. '/' .. a:name .. g:killersheep_sound_ext 551 | if has('sound') 552 | call sound_playfile(fname) 553 | elseif len(s:sound_cmd) 554 | call system(s:sound_cmd .. ' ' .. fname .. '&') 555 | endif 556 | endfunc 557 | 558 | func s:BlinkLevel(winid, on) 559 | call popup_setoptions(a:winid, #{highlight: a:on ? 'KillerLevelX': 'KillerLevel'}) 560 | let s:blink_timer = timer_start(s:blink_time, {x -> s:BlinkLevel(a:winid, !a:on)}) 561 | endfunc 562 | 563 | func s:PlaySoundForEnd() 564 | let s:frozen = 1 565 | if s:sheepcount == 0 566 | call s:PlaySound('win') 567 | if s:round == 5 568 | echomsg 'Amazing, you made it through ALL levels! (did you cheat???)' 569 | let s:end_timer = timer_start(2000, {x -> s:Clear()}) 570 | else 571 | call popup_settext(s:levelid, 'Level ' .. (s:round + 1)) 572 | call s:BlinkLevel(s:levelid, 1) 573 | call timer_start(2000, {x -> s:NextRound()}) 574 | endif 575 | else 576 | call s:StopMusic() 577 | call s:PlaySound('fail') 578 | let s:end_timer = timer_start(4000, {x -> s:Clear()}) 579 | endif 580 | endfunc 581 | 582 | func s:Clear() 583 | call s:StopMusic() 584 | if s:intro_timer 585 | call timer_stop(s:intro_timer) 586 | let s:intro_timer = 0 587 | endif 588 | call popup_clear() 589 | if get(s:, 'end_timer', 0) 590 | call timer_stop(s:end_timer) 591 | let s:end_timer = 0 592 | endif 593 | if get(s:, 'ready_timer', 0) 594 | call timer_stop(s:ready_timer) 595 | let s:ready_timer = 0 596 | endif 597 | if get(s:, 'poop_timer', 0) 598 | call timer_stop(s:poop_timer) 599 | let s:poop_timer = 0 600 | endif 601 | if get(s:, 'bullet_timer', 0) 602 | call timer_stop(s:bullet_timer) 603 | let s:bullet_timer = 0 604 | endif 605 | if get(s:, 'blink_timer', 0) 606 | call timer_stop(s:blink_timer) 607 | let s:blink_timer = 0 608 | endif 609 | endfunc 610 | -------------------------------------------------------------------------------- /plugin/beh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/beh.mp3 -------------------------------------------------------------------------------- /plugin/beh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/beh.ogg -------------------------------------------------------------------------------- /plugin/fail.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/fail.mp3 -------------------------------------------------------------------------------- /plugin/fail.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/fail.ogg -------------------------------------------------------------------------------- /plugin/fire.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/fire.mp3 -------------------------------------------------------------------------------- /plugin/fire.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/fire.ogg -------------------------------------------------------------------------------- /plugin/killersheep.vim: -------------------------------------------------------------------------------- 1 | " Silly game to show off new features in Vim 8.2. 2 | " Last Update: 2019 Dec 7 3 | " 4 | " Requirements: 5 | " - feature +textprop 6 | " - feature +sound or command "afplay", "paplay" or "cvlc". 7 | " - Vim patch level 8.1.1705 8 | " - terminal with at least 45 lines 9 | " 10 | " :KillKillKill start game 11 | " l move cannon right 12 | " h move cannot left 13 | " fire 14 | " exit game 15 | " 16 | " By default plays .ogg files on Unix, .mp3 files on MS-Windows. 17 | " Set g:killersheep_sound_ext to overrule, e.g.: 18 | " let g:killersheep_sound_ext = '.mp3' 19 | " 20 | " Thanks to my colleagues Greg, Martijn and Shannon for the sounds! 21 | " 22 | 23 | if get(g:, 'loaded_killersheep', 0) 24 | finish 25 | endif 26 | let g:loaded_killersheep = 1 27 | 28 | let s:dir = expand(':h') 29 | 30 | command KillKillKill call s:StartKillerSheep() 31 | 32 | func s:StartKillerSheep() 33 | " Check features before loading the autoload file to avoid error messages. 34 | if !has('patch-8.1.1705') 35 | call s:Sorry('Sorry, This build of Vim is too old, you need at least 8.1.1705') 36 | return 37 | endif 38 | if !has('textprop') 39 | call s:Sorry('Sorry, This build of Vim is lacking the +textprop feature') 40 | return 41 | endif 42 | if &lines < 45 43 | call s:Sorry('Need at least a terminal height of 45 lines') 44 | return 45 | endif 46 | 47 | " The implementation is in an autoload file, so that this plugin doesn't 48 | " take much time when not being used. 49 | call killersheep#Start(s:dir) 50 | endfunc 51 | 52 | func s:Sorry(msg) 53 | echohl WarningMsg 54 | echo a:msg 55 | echohl None 56 | endfunc 57 | -------------------------------------------------------------------------------- /plugin/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/music.mp3 -------------------------------------------------------------------------------- /plugin/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/music.ogg -------------------------------------------------------------------------------- /plugin/poop.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/poop.mp3 -------------------------------------------------------------------------------- /plugin/poop.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/poop.ogg -------------------------------------------------------------------------------- /plugin/quack.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/quack.mp3 -------------------------------------------------------------------------------- /plugin/quack.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/quack.ogg -------------------------------------------------------------------------------- /plugin/win.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/win.mp3 -------------------------------------------------------------------------------- /plugin/win.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim/killersheep/e91801129d2d4c26dd21b5d223690c6aa87c05ba/plugin/win.ogg --------------------------------------------------------------------------------