1const sleep = (milliseconds) => { 2 return new Promise(resolve => setTimeout(resolve, milliseconds)); 3} 4 5var map_of_running_scripts = []; 6function get_running_script(id) 7{ 8 let running_script=map_of_running_scripts[id]; 9 if(running_script == null) 10 { 11 running_script = { 12 id: id, 13 running: false, 14 stop_request: false, 15 action_button_released: false 16 }; 17 map_of_running_scripts[id]=running_script; 18 } 19 return running_script; 20} 21 22var execute_script = function(id, script_lang, action_script) { 23 let running_script=get_running_script(id); 24 if(running_script.running) 25 { 26 //alert("action script with "+id+" is still running, double execution is prevented... requesting stop"); 27 running_script.stop_request=true; 28 } 29 else 30 { 31 running_script.running=true; 32 running_script.stop_request=false; 33 34 setTimeout(async function() { 35 let button_element=$('#ck'+id); 36 button_element.css("background-color", "var(--red)"); 37 try{ 38 await parse_script(script_lang, action_script, true, id); 39 } 40 finally 41 { 42 if(button_element.length >0 && button_element[0].parentElement == null) 43 {//if the button_element is decoupled because button was redrawn as a new DOM instance 44 button_element=$('#ck'+id); //then get the new dom element 45 } 46 button_element.css("background-color", ""); 47 48 let running_script=get_running_script(id); 49 running_script.running = false; 50 running_script.stop_request=false; 51 } 52 }); 53 } 54} 55 56function stop_all_scripts() 57{ 58 for(id in map_of_running_scripts) 59 { 60 let action_button = custom_keys.find(el => el.id == id); 61 if(action_button === undefined || 62 action_button.auto_started !== undefined) 63 {//skip auto started scripts 64 continue; 65 } 66 67 let running_script=get_running_script(id); 68 running_script.stop_request=true; 69 } 70} 71 72function not_stopped(id) 73{ 74 return id<0 ? true : get_running_script(id).stop_request == false; 75} 76 77async function parse_script(script_lang, action_script, execute = false, execution_id = -1) { 78 action_script = action_script.trim(); 79 if(action_script.length==0) 80 { 81 $('#action_button_syntax_error').html('you have to enter at least one action ...'); 82 return false; 83 } 84 if(script_lang == 'javascript') 85 { 86 var valid = true; 87 var js_script=action_script; 88 var js_script_function; 89 let AsyncFunction = Object.getPrototypeOf(async function(){}).constructor 90 try { 91 js_script_function=new AsyncFunction(`let this_id=${execution_id};let still_active=function(){return not_stopped(this_id);};${js_script}`); 92 } catch (error) { 93 valid=false; 94 if(execute==false) 95 { 96 $('#action_button_syntax_error').html(error.message); 97 } 98 } 99 if(execute) 100 { 101 await js_script_function(); 102 } 103 } 104 else 105 { 106 valid = await execute_action_sequence_script(action_script, execute, execution_id); 107 } 108 return valid; 109}; 110 111async function action(cmd, execute=true) 112{ 113 await execute_action_sequence_script(cmd, execute); 114} 115 116 117async function execute_action_sequence_script(action_script, execute=false, execution_id=-1) 118{ 119 action_script=action_script.replace(/[{]/g,'{=>') 120 action_script=action_script.replace(/[}]/g,'=>}') 121 var cmd_sequence = action_script.split('=>'); 122 var valid = true; 123 var error_message = ""; 124 125 var pc=0; 126 var pc_loop_begin=[]; 127 var lc=[]; 128 var loop_depth=0; 129 130 while (pc < cmd_sequence.length && not_stopped(execution_id)) { 131 var cmd = cmd_sequence[pc].trim(); 132 pc++; 133 134 if(cmd.match(/^loop[0-9]+[{]$/) != null) 135 { 136 loop_depth++; 137 lc[loop_depth]=execute ? parseInt(cmd.match(/[0-9]+/)) : 1; 138 pc_loop_begin[loop_depth]=pc; 139 } 140 else if(cmd.length == 1 && cmd =='}') 141 { 142 lc[loop_depth]--; 143 if(lc[loop_depth]>0) 144 { 145 pc=pc_loop_begin[loop_depth]; 146 } 147 else 148 { 149 loop_depth--; 150 if(loop_depth<0) 151 { 152 error_message="too many closing loop bracket at pc="+pc; 153 valid=false; 154 break; 155 } 156 } 157 } 158 else if(await execute_single_action(cmd, execute, execution_id)==true) 159 { 160 } 161 else 162 { 163 error_message='unknown command "'+cmd+'" at pc=' +pc; 164 valid = false;
165 break; 166 } 167 } 168 169 if(loop_depth>0) 170 { 171 error_message="missing closing loop "+pc_loop_begin+" brackets"; 172 valid=false; 173 } 174 175 if(execute == false) 176 { 177 $('#action_button_syntax_error').html(error_message); 178 } 179 return valid; 180} 181 182 183async function execute_single_action(cmd, execute=true, execution_id=-1) 184{ 185 cmd=cmd.trim(); 186 var valid = true; 187 var joy_cmd_tokens=null; 188 if(cmd.match(/^'.+?'$/sm) != null) 189 { 190 if(execute) 191 { 192 var chars = cmd.substring(1,cmd.length-1);//.split(""); 193 var time_to_emit_next_char = 100; 194// emit_string(chars,0,time_to_emit_next_char); 195 wasm_auto_type(chars); 196 //blocking execution of action script and wait for all keys emitted 197 await sleep(time_to_emit_next_char*chars.length); 198 } 199 } 200 else if(cmd == 'pause') 201 { 202 if(execute) 203 { 204 if(is_running()) 205 { 206 app.button_run_click(); 207 } 208 } 209 210 } 211 else if(cmd == 'run') 212 { 213 if(execute) 214 { 215 if(!is_running()) 216 { 217 app.button_run_click(); 218 } 219 } 220 } 221 else if(cmd == 'toggle_run') 222 { 223 if(execute) 224 { 225 app.button_run_click(); 226 } 227 } 228 else if(cmd == 'toggle_warp') 229 { 230 if(execute) 231 {//0==auto, 1==never, 2==always 232 let mode = wasm_get_config("OPT_C64_WARP_MODE"); 233 if(mode <= 1) auto_or_never_warp_mode = mode ==0 ?"WARP_AUTO":"WARP_NEVER"; 234 wasm_configure('OPT_C64_WARP_MODE', mode<=1 ? 2/*'WARP_ALWAYS'*/: auto_or_never_warp_mode==="WARP_AUTO"?0:1); 235 } 236 } 237 else if(cmd == 'toggle_speed') 238 { 239 if(execute) 240 { 241 $('#button_speed_toggle').click(); 242 } 243 } 244 else if(cmd.match(/^[0-9]+ms$/) != null) 245 { 246 if(execute) 247 { 248 await sleep(parseInt(cmd.match(/[0-9]+/))); 249 } 250 } 251 else if(cmd == 'take_snapshot') 252 { 253 if(execute) 254 { 255 $('#button_take_snapshot').click(); 256 } 257 } 258 else if(cmd == 'keyboard') 259 { 260 if(execute) 261 { 262 app.button_keyboard_click(); 263 } 264 } 265 else if(cmd == 'menubar') 266 { 267 if(execute) 268 { 269 app.button_show_menu_click(); 270 } 271 } 272 else if(cmd == 'datasette_play') 273 { 274 if(execute) 275 { 276 wasm_press_play(); 277 } 278 } 279 else if(cmd == 'datasette_stop') 280 { 281 if(execute) 282 { 283 wasm_press_stop(); 284 } 285 } 286 else if(cmd == 'datasette_rewind') 287 { 288 if(execute) 289 { 290 wasm_rewind(); 291 } 292 } 293 else if(cmd == 'clipboard_paste') 294 { 295 if(execute) 296 { 297 let cliptext = await navigator.clipboard.readText(); 298 action(`'${cliptext.toLowerCase()}'`); 299 } 300 } 301 else if(cmd == 'fullscreen') 302 { 303 if(execute) 304 { 305 app.button_fullscreen_click(); 306 } 307 } 308 else if(cmd == 'restore_last_snapshot') 309 { 310 if(execute) 311 { 312 await load_last_snapshot(); 313 } 314 } 315 else if(cmd == 'swap_joystick') 316 { 317 if(execute) 318 { 319 var port1_value=port1; 320 port1=port2; 321 port2=port1_value; 322 port1_value= $('#port1').val(); 323 $('#port1').val($('#port2').val()); 324 $('#port2').val(port1_value); 325 } 326 } 327 else if( 328 ( 329 joy_cmd_tokens=cmd.match(/^j([12])(fire|up|down|right|left)([01])$/) 330 ) 331 != null 332 ) 333 { 334 if(execute) 335 { 336 execute_joystick_script(joy_cmd_tokens); 337 } 338 } 339 else if(translateKey2(cmd,cmd) !== undefined) 340 {//one single char 341 if(execute) 342 { 343 emit_key(cmd,0,100); 344 } 345 } 346 else if( 347 cmd.startsWith("press") 348 && 349 translateKey2(cmd.replace("press",""),cmd.replace("press","")) !== undefined 350 ) 351 { 352 if(execute) 353 { 354 press_key(cmd.replace("press","")); 355 } 356 } 357 else if( 358 cmd.startsWith("release") 359 && 360 translateKey2(cmd.replace("release",""),cmd.replace("release","")) !== undefined 361 ) 362 { 363 if(execute) 364 { 365 release_key(cmd.replace("release","")); 366 } 367 } 368 else if(cmd == "await_action_button_released") 369 { 370 if(execute) 371 { 372 await action_button_released(execution_id); 373 } 374 } 375 else if(cmd == 'toggle_action_buttons') 376 { 377 if(execute) 378 { 379 let this_buttons_label = $('#ck'+execution_id).text(); 380 $(".custom_key").not(`":contains('${this_buttons_label}')"`).toggle(); 381 } 382 } 383 else 384 { 385 valid=false; 386 } 387 return valid; 388} 389 390 391 392async function validate_custom_key_form(){ 393 var is_valid=true; 394 is_valid &=await validate_custom_key(); 395 is_valid &=await validate_action_script(); 396 return is_valid; 397} 398 399async function reset_validate(){ 400 document.querySelectorAll(".is-invalid").forEach(el=>el.classList.remove("is-invalid")) 401 document.querySelectorAll(".is-valid").forEach(el=>el.classList.remove("is-valid")) 402} 403 404async function validate_custom_key(){ 405 var is_valid=true; 406 var input_button_text=$('#input_button_text'); 407 var input_button_shortcut=$('#input_button_shortcut'); 408 409 input_button_text.removeClass("is-valid").removeClass("is-invalid"); 410 input_button_shortcut.removeClass("is-valid").removeClass("is-invalid"); 411 412 if( input_button_text.val().trim().length==0 && 413 input_button_shortcut.val().trim().length==0) 414 { 415 input_button_text.addClass("is-invalid"); 416 input_button_shortcut.addClass("is-invalid"); 417 is_valid=false; 418 } 419 else 420 { 421 input_button_text.addClass("is-valid"); 422 input_button_shortcut.addClass("is-valid"); 423 } 424 425 return is_valid; 426}; 427async function validate_action_script() 428{ 429 let is_valid=true; 430 let input_action_script=$('#input_action_script'); 431 let script_lang = $('#button_script_language').text(); 432 input_action_script.removeClass("is-valid").removeClass("is-invalid"); 433 if( (await parse_script(script_lang, input_action_script.val())) == false) 434 { 435 input_action_script.addClass("is-invalid"); 436 is_valid=false; 437 } 438 else 439 { 440 input_action_script.addClass("is-valid"); 441 } 442 return is_valid; 443}; 444 445 446 447function execute_joystick_script(cmd_tokens) 448{ 449 var portnr=cmd_tokens[1]; 450 var dir= cmd_tokens[2].toUpperCase(); 451 var down_or_release = cmd_tokens[3]; 452 453 var previous_owner = set_port_owner(portnr,PORT_ACCESSOR.BOT); 454 var native_joy_cmd=null; 455 if(dir == "FIRE") 456 { 457 native_joy_cmd= portnr+(down_or_release == 1 ?"PRESS_"+dir:"RELEASE_"+dir); 458 } 459 else 460 { 461 native_joy_cmd= portnr+(down_or_release == 1 ?"PULL_"+dir:"RELEASE_"+((dir=="LEFT" || dir=="RIGHT")?"X":"Y")); 462 } 463 send_joystick(PORT_ACCESSOR.BOT,portnr,native_joy_cmd); 464 465 set_port_owner(portnr,previous_owner); 466 } 467 468 async function load_last_snapshot() 469 { 470 let app_snaps = await get_snapshots_for_app_title(global_apptitle); 471 if(app_snaps.length>0) 472 { 473 var snapshot = app_snaps[app_snaps.length-1]; 474 if(is_running()) 475 { 476 wasm_halt(); 477 } 478 wasm_loadfile( 479 snapshot.title+".vc64", 480 snapshot.data, 481 snapshot.data.length); 482 if(!is_running()) 483 { 484 app.button_run_click(); 485 } 486 { 487 wasm_run(); 488 } 489 } 490 } 491 492 function sprite_xpos(sprite_id) 493 { 494 var all_sprite_pos = wasm_sprite_info().split(","); 495 return all_sprite_pos[sprite_id*2]; 496 } 497 498 function sprite_ypos(sprite_id) 499 { 500 var all_sprite_pos = wasm_sprite_info().split(","); 501 return all_sprite_pos[sprite_id*2+1]; 502 } 503 504 505class BotEvent { 506 log() { 507 console.log(`BotEvent { other_sprite=${this.other_sprite}, x_dir=${this.x_dir}, y_dir=${this.y_dir}, distance=${this.distance}}`) 508 } 509} 510 511class Bot { 512 constructor(proccess_id, sprite_id, min_diagonal_range_angle=2
5126, max_diagonal_range_angle=28) { 513 this.sprite_id = sprite_id; 514 this.process_id = proccess_id; 515 this.min_diagonal_range_angle=min_diagonal_range_angle; 516 this.max_diagonal_range_angle=max_diagonal_range_angle; 517 this.process_event=false; 518 } 519 fetch_sprite_info(){ 520 this.all_sprite_pos = wasm_sprite_info().split(","); 521 } 522 523 524 get _x() { 525 return this.x(this.sprite_id); 526 } 527 get _y() { 528 return this.y(this.sprite_id); 529 } 530 531 get port() { 532 this.controller_port; 533 } 534 535 next_event(bot_event){ 536 console.log(` event=${bot_event.other_sprite} ${bot_event.x_dir} ${bot_event.y_dir} \n`); 537 } 538 539 distance(other_sprite) { 540 const dx = this._x - this.x(other_sprite); 541 const dy = this._y - this.y(other_sprite); 542 return Math.hypot(dx, dy); 543 } 544 545 x(sprite){ return this.all_sprite_pos[sprite*2]}; 546 y(sprite){ return this.all_sprite_pos[sprite*2+1]}; 547 548 async aim_and_shoot(bot_event) { 549 var x_aim = bot_event.x_dir; 550 var y_aim = bot_event.y_dir; 551 552 if(x_aim != null || y_aim != null) 553 { 554 set_port_owner(port, 555 PORT_ACCESSOR.BOT); 556 await action(`j${port}left0=>j${port}up0`); 557 558 await action(`j${port}fire1`); 559 if(x_aim != null) 560 await action(`j${port}${x_aim}1`); 561 if(y_aim != null) 562 await action(`j${port}${y_aim}1`); 563 await action("60ms"); 564 if(x_aim != null) 565 await action(`j${port}${x_aim}0`); 566 if(y_aim != null) 567 await action(`j${port}${y_aim}0`); 568 await action(`j${port}fire0`); 569 await action("60ms"); 570 571 set_port_owner( 572 port, 573 PORT_ACCESSOR.MANUAL 574 ); 575 } 576 577 } 578 579 move(direction, time) { 580 581 } 582 583 sprite_in_shooting_range(other_sprite) 584 { 585 var y_light=this.y(this.sprite_id); 586 var y_dark=this.y(other_sprite); 587 var x_light=this.x(this.sprite_id); 588 var x_dark=this.x(other_sprite); 589 590 var y_diff=Math.abs(y_light - y_dark); 591 var x_diff=Math.abs(x_light - x_dark); 592 var angle = Math.atan2(y_diff, x_diff) * 180 / Math.PI; 593 594 var x_aim=null; 595 var y_aim=null; 596 if( y_diff<10 || this.min_diagonal_range_angle<angle && angle<this.max_diagonal_range_angle ) 597 { 598 var x_rel = x_light-x_dark; 599 x_aim=x_rel > 0 ?'left':'right'; 600 } 601 if( x_diff <10 || this.min_diagonal_range_angle<angle && angle<this.max_diagonal_range_angle) 602 {
603 var y_rel = y_light-y_dark; 604 y_aim=y_rel > 0 ?'up':'down'; 605 } 606 if(x_aim != null || y_aim != null) 607 { 608 var event = new BotEvent(); 609 event.other_sprite = other_sprite; 610 event.x_dir = x_aim; 611 event.y_dir = y_aim; 612 event.distance = this.distance(other_sprite); 613 return event; 614 } 615 else 616 { 617 return null; 618 } 619 } 620 621 async run(controller_port=1) { 622 this.controller_port=controller_port; 623 624 while(not_stopped(this.process_id)) 625 { 626 //check for events 627 this.fetch_sprite_info(); 628 for(var id = 0; id<8; id++) 629 { 630 if(id != this.sprite_id) 631 { 632 var sprite_event = await this.sprite_in_shooting_range(id); 633 if(sprite_event != null) 634 { 635 this.next_event(sprite_event); 636 } 637 } 638 } 639 await action("20ms"); // time of one frame 1000/50=20ms 640 } 641 } 642} 643 644 645class ArchonBot extends Bot { 646 async next_event(event) { 647 if( event.other_sprite == 1 ) 648 { 649 this.aim_and_shoot(event); 650 } 651 } 652} 653 654class FalconBot extends Bot { 655 constructor(proccess_id, sprite_id) { 656 super(proccess_id, sprite_id); 657 this.min_diagonal_range_angle = 30; 658 this.max_diagonal_range_angle = 35; 659 } 660 661 async next_event(event) { 662 if( 0<event.other_sprite ) 663 { 664 event.log(); 665 666 if(this._y > 100) 667 {//prevent crash on ground 668 await action("j1up1=>300ms=>j1up0"); 669 } 670 else if(event.y_dir == 'up' ) 671 { 672 } 673 else if(event.x_dir == 'left' //|| event.x_dir == 'right' 674 && event.distance < 150 /*&& this.process_event == false */ 675 ) 676 { 677 this.process_event = true; 678 //await this.aim_and_shoot(event); 679 if(event.y_dir == 'down') 680 { 681 await action("j1down1"); 682 } 683 684 await action("j1fire1=>100ms=>j1fire0") 685 686 if(event.y_dir == 'down') 687 { 688 await action("j1down0=>10ms=>j1up1=>60ms=>j1up0"); 689 } 690 console.log("shoot"); 691 692 this.process_event = false; 693 } 694 else 695 { 696 console.log("no shoot"); 697 } 698 } 699 } 700} 701 702function wasm_runstop_restore() 703{ 704 wasm_schedule_key(7, 7, 1, 0); //press runstop 705 wasm_schedule_key(9, 9, 1, 2); //press restore 706 wasm_schedule_key(9, 9, 0, 4); //release restore 707 wasm_schedule_key(7, 7, 0, 6); //release runstop 708} 709 710 711function press_key(key) 712{ 713 let c64code=translateKey2(key, key); 714 if(c64code !== undefined ) 715 { 716 wasm_schedule_key(c64code.raw_key[0], c64code.raw_key[1], 1, 0); 717 if(c64code.modifier != null) 718 { 719 wasm_schedule_key(c64code.modifier[0], c64code.modifier[1], 1, 0); 720 } 721 } 722} 723function release_key(key) 724{ 725 let c64code=translateKey2(key, key); 726 if(c64code !== undefined ) 727 { 728 wasm_schedule_key(c64code.raw_key[0], c64code.raw_key[1], 0, 0); 729 if(c64code.modifier != null) 730 { 731 wasm_schedule_key(c64code.modifier[0], c64code.modifier[1], 0, 0); 732 } 733 } 734} 735 736async function wasm_ready_after_reset() 737{ 738 let mega_roms=JSON.parse(wasm_rom_info()).kernal.startsWith("mega"); 739 //mega roms is ready after 500000 cycles, original C= Rom is ready after 2700000 cycles 740 while(wasm_get_cpu_cycles()<(mega_roms?500000:2700000)) 741 { 742 await sleep(50); 743 } 744} 745 746async function action_button_released(id) 747{ 748 while(!get_running_script(id).action_button_released) 749 { 750 await sleep(50); 751 } 752} 753 754//the following line is only there for codemirrors jshint autocomplete feature 755//this_id and still_active will be overscoped by their equal named pendants in the action blocks 756var this_id=0; var still_active=function(){alert("NEVER CALLED!!")}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.