if (!confirm('s블록을 허용하시겠습니까?')) { alert('s블록 로드가 취소되었습니다.'); throw new Error('User cancelled s블록 loading'); } // 기존 Entry.staticBlocks가 없을 경우에만 fallback으로 초기화 (덮어쓰지 않음) if (!Entry.staticBlocks || Entry.staticBlocks.length === 0) { Entry.staticBlocks = EntryStatic.getAllBlocks ? EntryStatic.getAllBlocks() : []; } EntryStatic.getAllBlocks = () => Entry.staticBlocks; const updateCategory = (category, options) => { // 기존 카테고리 목록을 가져와서 s블록이 없을 때만 추가 const blockMenu = Entry.playground.blockMenu || Entry.playground.mainWorkspace.blockMenu; const existingCategories = (blockMenu._categories || []).map(c => ({ category: c, visible: true })); const alreadyExists = existingCategories.some(c => c.category === category); if (!alreadyExists) { existingCategories.push({ category: category, visible: true }); } blockMenu._generateCategoryView(existingCategories); for (let i = 0; i < $('.entryCategoryElementWorkspace').length; i++) { if (!($($('.entryCategoryElementWorkspace')[i]).attr('id') == 'entryCategorytext')) { $($('.entryCategoryElementWorkspace')[i]).attr('class', 'entryCategoryElementWorkspace'); } } // _categoryData를 덮어쓰지 않고, 새 카테고리 데이터만 병합 const currentData = blockMenu._categoryData || []; const alreadyInData = currentData.some(d => d.category === category); if (!alreadyInData) { const newCategoryData = Entry.staticBlocks.find(b => b.category === category); if (newCategoryData) currentData.push(newCategoryData); blockMenu._categoryData = currentData; } blockMenu._generateCategoryCode(category); if (options) { if (options.background) { $(`#entryCategory${category}`).css({ 'background-image': `url(${options.background})`, 'background-repeat': 'no-repeat' }); if (options.backgroundSize) $(`#entryCategory${category}`).css('background-size', options.backgroundSize + 'px'); } if (options.name) $(`#entryCategory${category}`)[0].innerText = options.name; } }; const addBlock = (blockname, template, color, params, _class, func, skeleton = 'basic') => { Entry.block[blockname] = { color: color.color, outerLine: color.outerline, fontColor: color.fontColor, skeleton: skeleton, statement: [], params: params.params, events: {}, def: { params: params.def, type: blockname }, paramsKeyMap: params.map, class: _class || 'default', func: func, template: template }; }; const HW_ICON = 'block_icon/hardware_icon.svg'; const START_ICON = 'block_icon/start_icon_play.svg'; // ─── 타이틀 블록 헬퍼 ─── const addTitle = (name, text) => { Entry.block[name] = { color: EntryStatic.colorSet.common.TRANSPARENT, outerLine: EntryStatic.colorSet.common.TRANSPARENT, skeleton: 'basic_text', statement: [], params: [{ type: 'Text', text, color: EntryStatic.colorSet.common.TEXT, class: 'bold', align: 'center' }], events: {}, def: { params: [], type: name }, paramsKeyMap: {}, class: 's블록_title', func: (sprite, script) => script.callReturn(), template: '%1' }; }; addTitle('s블록_title_기본', '기본'); addTitle('s블록_title_팝업', '팝업 / 알림 / 클립보드'); addTitle('s블록_title_브라우저', '브라우저 / 페이지'); addTitle('s블록_title_콘솔', '콘솔'); addTitle('s블록_title_작품제어', '작품 제어'); addTitle('s블록_title_엔트리페이지', '엔트리 페이지 / 검색'); addTitle('s블록_title_저장소', '컴퓨터 저장소'); addTitle('s블록_title_iframe', 'iframe'); addTitle('s블록_title_수학', '수학 계산'); addTitle('s블록_title_날짜', '날짜 / 시간'); addTitle('s블록_title_장면', '장면 제어'); addTitle('s블록_title_변수', '변수 / 리스트'); addTitle('s블록_title_이펙트', '이펙트'); addTitle('s블록_title_텍스트', '텍스트'); // ← 새로 추가 addTitle('s블록_title_오브젝트', '오브젝트'); addTitle('s블록_title_재미', '재미'); addTitle('s블록_title_시스템', '시스템'); // ═══════════════════════════════════════════════ // 기본 유틸 // ═══════════════════════════════════════════════ // 주석 블록 addBlock('s블록_주석', '%1주석', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: START_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => script.callReturn(), 'basic_event'); // 메모 블록 addBlock('s블록_메모', '그림그리기: %1 %2', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Text' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['메모블록 만들다가 오류나서 좋은걸 얻음'], map: { MEMO: 0 } }, 's블록', (sprite, script) => { return script.callReturn(); }); // 복제본인가? addBlock('s블록_복제본인가', '복제본인가?', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return sprite.isClone === true; }, 'basic_boolean_field'); // 클립보드 값 addBlock('s블록_클립보드값', '현재 클립보드의 값', { color: '#E67E22', outerline: '#CA6F1E', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', async (sprite, script) => { try { if (navigator.clipboard && navigator.clipboard.readText) { const text = await navigator.clipboard.readText(); return text; } else { return ''; } } catch(e) { console.error('클립보드 읽기 실패:', e); return ''; } }, 'basic_string_field'); // 참/거짓 addBlock('s블록_참거짓', '%1', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['참', 'true'], ['거짓', 'false']], value: 'true', fontSize: 11 }], def: ['true'], map: { VALUE: 0 } }, 's블록', (sprite, script) => { return script.getField('VALUE', script) === 'true'; }, 'basic_boolean_field'); // NaN/특수값 addBlock('s블록_NaN값', '%1', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [ ['NaN', 'NaN'], ['Infinity', 'Infinity'], ['-Infinity', '-Infinity'], ['null', 'null'], ['undefined', 'undefined'], ['-0', '-0'] ], value: 'NaN', fontSize: 11 }], def: ['NaN'], map: { TYPE: 0 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); switch(type) { case 'NaN': return NaN; case 'Infinity': return Infinity; case '-Infinity': return -Infinity; case 'null': return null; case 'undefined': return undefined; case '-0': return -0; default: return NaN; } }, 'basic_string_field'); // 조건부 기다리기 addBlock('s블록_조건부기다리기', '만일 %1 이라면 %2 가 %3 기다리기 %4', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'boolean' }, { type: 'Block', accept: 'boolean' }, { type: 'Dropdown', options: [['될 때까지', 'until'], ['인 동안', 'while']], value: 'until', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [null, null, 'until', null], map: { IF_COND: 0, WAIT_COND: 1, TYPE: 2 } }, 's블록', (sprite, script) => { if (!script.getValue('IF_COND', script)) return script.callReturn(); const waitCond = script.getValue('WAIT_COND', script), type = script.getField('TYPE', script); if (type === 'until' && waitCond) return script.callReturn(); if (type === 'while' && !waitCond) return script.callReturn(); return script; }); // 스크롤 판단 addBlock('s블록_스크롤판단', '%1 스크롤 하였는가?', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['아래로', 'down'], ['위로', 'up']], value: 'down', fontSize: 11 }], def: ['down'], map: { DIR: 0 } }, 's블록', (sprite, script) => { if (!window.s블록_스크롤_초기화) { window.s블록_스크롤_마지막Y = window.scrollY; window.s블록_스크롤_방향 = null; window.addEventListener('wheel', (e) => { window.s블록_스크롤_방향 = e.deltaY > 0 ? 'down' : 'up'; clearTimeout(window.s블록_스크롤_타이머); window.s블록_스크롤_타이머 = setTimeout(() => { window.s블록_스크롤_방향 = null; }, 200); }); window.s블록_스크롤_초기화 = true; } const dir = script.getField('DIR', script); return window.s블록_스크롤_방향 === dir; }, 'basic_boolean_field'); // ═══════════════════════════════════════════════ // 팝업 / 알림 // ═══════════════════════════════════════════════ // 알림창 addBlock('s블록_경고창', '%1 알림창 띄우기 %2', { color: '#E67E22', outerline: '#CA6F1E', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['안녕하세요!'] }, null], map: { CONTENT: 0 } }, 's블록', (sprite, script) => { alert(script.getValue('CONTENT', script)); return script.callReturn(); }); // 선택/프롬프트 창 addBlock('s블록_선택프롬프트', '%1 내용에 %2 창을 띄웠을 때 답변', { color: '#E67E22', outerline: '#CA6F1E', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['선택창', 'confirm'], ['프롬프트', 'prompt']], value: 'confirm', fontSize: 11 }], def: [{ type: 'text', params: ['내용'] }, 'confirm'], map: { CONTENT: 0, TYPE: 1 } }, 's블록', (sprite, script) => { const content = script.getValue('CONTENT', script); const type = script.getField('TYPE', script); if (type === 'confirm') return confirm(content); else { const result = prompt(content); return result !== null ? result : ''; } }, 'basic_string_field'); // ═══════════════════════════════════════════════ // 클립보드 / 복사 // ═══════════════════════════════════════════════ // 복사하기 addBlock('s블록_복사하기', '%1 복사하기 %2', { color: '#E67E22', outerline: '#CA6F1E', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['복사할 내용'] }, null], map: { DATA: 0 } }, 's블록', (sprite, script) => { const t = document.createElement('textarea'); t.value = script.getValue('DATA', script); document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); return script.callReturn(); }); // ═══════════════════════════════════════════════ // 브라우저 / 페이지 조작 // ═══════════════════════════════════════════════ // 브라우저 이름 addBlock('s블록_브라우저이름', '현재 접속한 브라우저 이름', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { const ua = navigator.userAgent; if (ua.indexOf('Edg') > -1) return 'Microsoft Edge'; else if (ua.indexOf('Chrome') > -1) return 'Google Chrome'; else if (ua.indexOf('Safari') > -1) return 'Safari'; else if (ua.indexOf('Firefox') > -1) return 'Mozilla Firefox'; else if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident') > -1) return 'Internet Explorer'; else if (ua.indexOf('Opera') > -1 || ua.indexOf('OPR') > -1) return 'Opera'; else return 'Unknown Browser'; }, 'basic_string_field'); // 현재 링크 addBlock('s블록_현재링크', '현재 링크', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return window.location.href; }, 'basic_string_field'); // 페이지 이름 바꾸기 addBlock('s블록_페이지이름바꾸기', '페이지 이름을 %1 으로 바꾸기 %2', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['새 페이지 이름'] }, null], map: { NAME: 0 } }, 's블록', (sprite, script) => { document.title = script.getValue('NAME', script); return script.callReturn(); }); // 페이지 새로고침 addBlock('s블록_페이지새로고침', '이 페이지 새로고침하기 %1', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { location.reload(); return script.callReturn(); }); // 사이트 열기 addBlock('s블록_사이트열기', '%1 사이트 열기 %2', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['https://playentry.org'] }, null], map: { URL: 0 } }, 's블록', (sprite, script) => { let url = script.getValue('URL', script); if (!url.startsWith('http')) url = 'https://' + url; window.open(url, '_blank'); return script.callReturn(); }); // 엔트리 로그아웃 addBlock('s블록_엔트리로그아웃', '엔트리 로그아웃하기 %1', { color: '#17A589', outerline: '#148A72', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { window.open('https://playentry.org/signout', '_blank'); return script.callReturn(); }); // ═══════════════════════════════════════════════ // 콘솔 // ═══════════════════════════════════════════════ // 콘솔 입력 addBlock('s블록_콘솔입력', '콘솔에 %1 을(를) %2 하기 %3', { color: '#616A6B', outerline: '#4D5656', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['로그', 'log'], ['경고', 'warn'], ['에러', 'error']], value: 'log', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['메시지'] }, 'log', null], map: { MSG: 0, TYPE: 1 } }, 's블록', (sprite, script) => { const msg = script.getValue('MSG', script); const type = script.getField('TYPE', script); if (type === 'log') console.log(msg); else if (type === 'warn') console.warn(msg); else if (type === 'error') console.error(msg); return script.callReturn(); }); // 콘솔 지우기 addBlock('s블록_콘솔지우기', '콘솔 모두 지우기 %1', { color: '#616A6B', outerline: '#4D5656', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { console.clear(); return script.callReturn(); }); // ═══════════════════════════════════════════════ // 엔트리 작품 제어 // ═══════════════════════════════════════════════ // 작품 일시정지 addBlock('s블록_작품일시정지', '작품 일시정지하기 %1', { color: '#C0392B', outerline: '#A93226', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { Entry.engine.togglePause(); return script.callReturn(); }); // 작품 멈추기 addBlock('s블록_작품멈추기', '작품 멈추기 %1', { color: '#C0392B', outerline: '#A93226', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { Entry.engine.toggleStop(); return script.callReturn(); }); // 엔트리 콘솔 모두 지우기 addBlock('s블록_엔트리콘솔지우기', '엔트리 콘솔 모두 지우기 %1', { color: '#C0392B', outerline: '#A93226', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { if (Entry.console) { if (typeof Entry.console.clear === 'function') Entry.console.clear(); else if (Entry.console.output) Entry.console.output.innerHTML = ''; } const consoleEl = document.querySelector('.entryConsole, .console, [class*="console"]'); if (consoleEl) consoleEl.innerHTML = ''; return script.callReturn(); }); // 모든 블록 해제 addBlock('s블록_모든블록해제', '더미데이터 불러오기 %1', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { if (Entry.playground?.blockMenu?._bannedClass) { [...Entry.playground.blockMenu._bannedClass].forEach(c => Entry.playground.blockMenu.unbanClass(c)); Entry.playground.blockMenu._generateCategoryView(); alert("더미데이터가 활성화 되었습니다!"); } return script.callReturn(); }); // s블록 해제 addBlock('s블록_해제하기', 's블록 카테고리 해제하기 %1', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { Entry.staticBlocks = Entry.staticBlocks.filter(c => c.category !== 's블록'); $('#entryCategorys블록').remove(); $('style#sBlockStyle').remove(); return script.callReturn(); }); // 아이디를 가진 블록 가져오기 addBlock('s블록_아이디블록가져오기', '%1 아이디를 가진 블록 가져오기 %2', { color: '#0a0e83ff', outerline: '#10306aff', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['블록 아이디'] }, null], map: { ID: 0 } }, 's블록', (sprite, script) => { const input = script.getValue('ID', script); const p = Entry.exportProject(); const o = JSON.parse(p.objects[0].script || '[]'); if (input) { const types = input.split(',').map(t => t.trim()); types.forEach(type => { if (type) o.push([{ type: type }]); }); p.objects[0].script = JSON.stringify(o); Entry.clearProject(); Entry.loadProject(p); } return script.callReturn(); }, 'basic_without_next'); // ═══════════════════════════════════════════════ // 엔트리 페이지 이동 / 검색 // ═══════════════════════════════════════════════ // 엔트리 페이지 열기 addBlock('s블록_엔트리페이지열기', '엔트리 %1 열기 %2', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['엔트리이야기', 'story'], ['노하우&팁', 'tips'], ['묻고답하기', 'qna'], ['작품만들기', 'create'], ['메인', 'main']], value: 'story', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['story', null], map: { PAGE: 0 } }, 's블록', (sprite, script) => { const urls = { story: 'https://playentry.org/community/entrystory/list', tips: 'https://playentry.org/community/tips/list', qna: 'https://playentry.org/community/qna/list', create: 'https://playentry.org/project/create', main: 'https://playentry.org/' }; window.open(urls[script.getField('PAGE', script)], '_blank'); return script.callReturn(); }); // 탐험하기 열기 addBlock('s블록_탐험하기열기', '탐험하기 열기 %1', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { window.open('https://space.playentry.org/explore', '_blank'); return script.callReturn(); }); // 인기작 보러가기 addBlock('s블록_인작보러가기', '인작 보러가기 %1', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { window.open('https://playentry.org/project/list/popular?sort=ranked&term=all', '_blank'); return script.callReturn(); }); // 엔트리 검색 addBlock('s블록_엔트리검색', '엔트리에 %1 검색하기 %2', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['검색어'] }, null], map: { QUERY: 0 } }, 's블록', (sprite, script) => { window.open(`https://playentry.org/us?query=${encodeURIComponent(script.getValue('QUERY', script))}`, '_blank'); return script.callReturn(); }); // 엔트리 검색(확장) addBlock('s블록_엔트리검색확장', '%1 에서 %2 검색하기 %3', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['스선', 'staff'], ['인작', 'popular']], value: 'staff', fontSize: 11 }, { type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['staff', { type: 'text', params: ['검색어'] }, null], map: { TYPE: 0, QUERY: 1 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); const query = encodeURIComponent(script.getValue('QUERY', script)); if (type === 'staff') window.open(`https://playentry.org/project/list/staffpick?term=all&sort=score&query=${query}`, '_blank'); else window.open(`https://playentry.org/project/list/popular?term=all&sort=score&query=${query}`, '_blank'); return script.callReturn(); }); // 제작자 페이지 열기 addBlock('s블록_제작자페이지열기', '이 블록 제작자의 마이페이지 열기 %1', { color: '#8E44AD', outerline: '#7D3C98', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { window.open('https://playentry.org/profile/699c5bfab180b89204787c3f?sort=created&term=all&isOpen=all', '_blank'); return script.callReturn(); }); // ═══════════════════════════════════════════════ // localStorage (컴퓨터 저장소) // ═══════════════════════════════════════════════ // 간편 저장 addBlock('s블록_localStorage간편저장', '%1 을(를) 컴퓨터에 저장하기 %2', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['값'] }, null], map: { VALUE: 0 } }, 's블록', (sprite, script) => { try { localStorage.setItem('s블록_간편저장', script.getValue('VALUE', script)); } catch(e) { console.error('localStorage 저장 실패:', e); } return script.callReturn(); }); // 간편 확인 addBlock('s블록_localStorage간편확인', '%1 이(가) 컴퓨터에 저장되어있는가?', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'text', params: ['값'] }], map: { VALUE: 0 } }, 's블록', (sprite, script) => { try { return localStorage.getItem('s블록_간편저장') === script.getValue('VALUE', script); } catch(e) { console.error('localStorage 확인 실패:', e); return false; } }, 'basic_boolean_field'); // 간편 삭제 addBlock('s블록_localStorage간편삭제', '%1 을(를) 컴퓨터에서 삭제하기 %2', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['값'] }, null], map: { VALUE: 0 } }, 's블록', (sprite, script) => { const value = script.getValue('VALUE', script); try { if (localStorage.getItem('s블록_간편저장') === value) localStorage.removeItem('s블록_간편저장'); } catch(e) { console.error('localStorage 삭제 실패:', e); } return script.callReturn(); }); // key-value 저장 addBlock('s블록_localStorage저장', 'key: %1 value: %2 을 이 컴퓨터에 저장하기 %3', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['키'] }, { type: 'text', params: ['값'] }, null], map: { KEY: 0, VALUE: 1 } }, 's블록', (sprite, script) => { try { localStorage.setItem(script.getValue('KEY', script), script.getValue('VALUE', script)); } catch(e) { console.error('localStorage 저장 실패:', e); } return script.callReturn(); }); // key로 value 불러오기 addBlock('s블록_localStorage불러오기', '%1 키를 가진 value의 값', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'text', params: ['키'] }], map: { KEY: 0 } }, 's블록', (sprite, script) => { try { const value = localStorage.getItem(script.getValue('KEY', script)); return value !== null ? value : ''; } catch(e) { console.error('localStorage 불러오기 실패:', e); return ''; } }, 'basic_string_field'); // key-value 삭제 addBlock('s블록_localStorage삭제', '%1 키를 가진 key와 value 삭제하기 %2', { color: '#26A65B', outerline: '#1E8449', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['키'] }, null], map: { KEY: 0 } }, 's블록', (sprite, script) => { try { localStorage.removeItem(script.getValue('KEY', script)); } catch(e) { console.error('localStorage 삭제 실패:', e); } return script.callReturn(); }); // ═══════════════════════════════════════════════ // iframe // ═══════════════════════════════════════════════ // iframe 열기 addBlock('s블록_iframe하기', '%1 으로 iframe하기 %2', { color: '#5DADE2', outerline: '#3498DB', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['https://playentry.org'] }, null], map: { URL: 0 } }, 's블록', (sprite, script) => { let url = script.getValue('URL', script); if (!url.startsWith('http')) url = 'https://' + url; const existingIframe = document.getElementById('s블록_iframe'); if (existingIframe) existingIframe.remove(); const iframe = document.createElement('iframe'); iframe.id = 's블록_iframe'; iframe.src = url; iframe.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;border:none;z-index:9999;'; document.body.appendChild(iframe); return script.callReturn(); }); // iframe 닫기 addBlock('s블록_iframe해제하기', 'iframe 해제하기 %1', { color: '#5DADE2', outerline: '#3498DB', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { const iframe = document.getElementById('s블록_iframe'); if (iframe) iframe.remove(); return script.callReturn(); }); // iframe 상태 확인 addBlock('s블록_iframe상태인가', '현재 iframe 상태인가?', { color: '#5DADE2', outerline: '#3498DB', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return document.getElementById('s블록_iframe') !== null; }, 'basic_boolean_field'); // ═══════════════════════════════════════════════ // 수학 계산 // ═══════════════════════════════════════════════ // 참 판단 addBlock('s블록_참판단', '%1 이(가) %2 인가?', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['참', 'true'], ['거짓', 'false']], value: 'true', fontSize: 11 } ], def: [{ type: 'number', params: ['1'] }, 'true'], map: { VALUE: 0, TYPE: 1 } }, 's블록', (sprite, script) => { const value = script.getValue('VALUE', script); const type = script.getField('TYPE', script); const isTruthy = !( value === 0 || value === '0' || value === '' || value === false || value === 'false' || value === '[]' || value === null || value === 'null' || value === undefined || value === 'undefined' || value === 'NaN' || (typeof value === 'number' && isNaN(value)) ); return type === 'true' ? isTruthy : !isTruthy; }, 'basic_boolean_field'); // 수 판단 addBlock('s블록_수판단', '%1 이(가) %2 인가?', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [ ['홀수', 'odd'], ['짝수', 'even'], ['합성수', 'composite'], ['소수', 'prime'], ['단위수', 'unit'], ['소수점이 있는 수', 'decimal'], ['음수', 'negative'], ['양수', 'positive'] ], value: 'odd', fontSize: 11 } ], def: [{ type: 'number', params: ['1'] }, 'odd'], map: { VALUE: 0, TYPE: 1 } }, 's블록', (sprite, script) => { const value = Number(script.getValue('VALUE', script)); const type = script.getField('TYPE', script); switch(type) { case 'odd': return value % 2 !== 0; case 'even': return value % 2 === 0; case 'composite': { if (value < 4) return false; const num = Math.floor(Math.abs(value)); for (let i = 2; i <= Math.sqrt(num); i++) { if (num % i === 0) return true; } return false; } case 'prime': { if (value < 2) return false; if (value === 2) return true; if (value % 2 === 0) return false; const n = Math.floor(Math.abs(value)); for (let i = 3; i <= Math.sqrt(n); i += 2) { if (n % i === 0) return false; } return true; } case 'unit': return value === 1; case 'decimal': return value % 1 !== 0; case 'negative': return value < 0; case 'positive': return value > 0; default: return false; } }, 'basic_boolean_field'); // 약수 개수 addBlock('s블록_약수개수', '%1 의 약수 개수', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'number', params: ['12'] }], map: { VALUE: 0 } }, 's블록', (sprite, script) => { const num = Math.abs(Math.floor(Number(script.getValue('VALUE', script)))); if (num === 0) return 0; if (num === 1) return 1; let count = 0; for (let i = 1; i <= num; i++) { if (num % i === 0) count++; } return count; }, 'basic_string_field'); // 두 점 거리 addBlock('s블록_두점거리', '%1 과 %2 의 거리', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }], def: [{ type: 'number', params: ['3'] }, { type: 'number', params: ['7'] }], map: { NUM1: 0, NUM2: 1 } }, 's블록', (sprite, script) => { return Math.abs(Number(script.getValue('NUM2', script)) - Number(script.getValue('NUM1', script))); }, 'basic_string_field'); // 진법 변환 addBlock('s블록_진법변환', '%1 을(를) %2 에서 %3 로 반환한 값', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['원래', 'original'], ['이진법', 'binary'], ['십진법', 'decimal'], ['유니코드', 'unicode'], ['아스키코드', 'ascii']], value: 'original', fontSize: 11 }, { type: 'Dropdown', options: [['원래', 'original'], ['이진법', 'binary'], ['십진법', 'decimal'], ['유니코드', 'unicode'], ['아스키코드', 'ascii']], value: 'binary', fontSize: 11 } ], def: [{ type: 'number', params: ['10'] }, 'original', 'binary'], map: { VALUE: 0, FROM: 1, TO: 2 } }, 's블록', (sprite, script) => { const value = script.getValue('VALUE', script); const from = script.getField('FROM', script); const to = script.getField('TO', script); if (from === to) return value; let intermediate = value; switch(from) { case 'original': intermediate = value; break; case 'binary': intermediate = parseInt(String(value), 2); if (isNaN(intermediate)) intermediate = 0; break; case 'decimal': intermediate = Number(value); if (isNaN(intermediate)) intermediate = 0; break; case 'unicode': case 'ascii': intermediate = String(value).split(',').map(c => String.fromCharCode(parseInt(c.trim()))).join(''); break; } switch(to) { case 'original': return intermediate; case 'binary': const num = Number(intermediate); if (isNaN(num)) return '0'; return num.toString(2); case 'decimal': return Number(intermediate); case 'unicode': case 'ascii': const text = String(intermediate); if (text.length === 0) return ''; if (text.length === 1) return text.charCodeAt(0); return Array.from(text).map(char => char.charCodeAt(0)).join(','); } return value; }, 'basic_string_field'); // 삼항 조건식 addBlock('s블록_삼항조건식', '%1 %2 %3 ? %4 : %5', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['=', '=='], ['≠', '!='], ['>', '>'], ['<', '<'], ['≥', '>='], ['≤', '<=']], value: '==', fontSize: 11 }, { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' } ], def: [{ type: 'number', params: ['0'] }, '==', { type: 'number', params: ['0'] }, { type: 'text', params: ['참'] }, { type: 'text', params: ['거짓'] }], map: { LEFT: 0, OP: 1, RIGHT: 2, TRUE_VAL: 3, FALSE_VAL: 4 } }, 's블록', (sprite, script) => { const left = script.getValue('LEFT', script), op = script.getField('OP', script), right = script.getValue('RIGHT', script); const trueVal = script.getValue('TRUE_VAL', script), falseVal = script.getValue('FALSE_VAL', script); const leftNum = Number(left), rightNum = Number(right), useNum = !isNaN(leftNum) && !isNaN(rightNum); let result = false; switch(op) { case '==': result = useNum ? leftNum === rightNum : left === right; break; case '!=': result = useNum ? leftNum !== rightNum : left !== right; break; case '>': result = useNum ? leftNum > rightNum : left > right; break; case '<': result = useNum ? leftNum < rightNum : left < right; break; case '>=': result = useNum ? leftNum >= rightNum : left >= right; break; case '<=': result = useNum ? leftNum <= rightNum : left <= right; break; } return result ? trueVal : falseVal; }, 'basic_string_field'); // 배열 항목 수 addBlock('s블록_배열항목수', '%1 배열의 항목수', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'text', params: ['[1,2,3]'] }], map: { ARRAY: 0 } }, 's블록', (sprite, script) => { try { const array = JSON.parse(script.getValue('ARRAY', script)); return Array.isArray(array) ? array.length : 0; } catch(e) { return 0; } }, 'basic_string_field'); // 대학수학 연산 블록 (값블록) addBlock('s블록_대학수학', '%1 의 %2', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [ ['sin(라디안)', 'sin'], ['cos(라디안)', 'cos'], ['tan(라디안)', 'tan'], ['arcsin', 'asin'], ['arccos', 'acos'], ['arctan', 'atan'], ['sinh(쌍곡선)', 'sinh'], ['cosh(쌍곡선)', 'cosh'], ['tanh(쌍곡선)', 'tanh'], ['자연로그 ln', 'log'], ['상용로그 log₁₀', 'log10'], ['log₂', 'log2'], ['e^x (지수)', 'exp'], ['계승 n!', 'factorial'], ['감마함수 Γ(n)', 'gamma'], ['제곱근', 'sqrt'], ['세제곱근', 'cbrt'], ['절댓값', 'abs'], ['올림', 'ceil'], ['내림', 'floor'], ['반올림', 'round'], ['부호 (±1,0)', 'sign'] ], value: 'sin', fontSize: 11 } ], def: [{ type: 'number', params: ['1'] }, 'sin'], map: { VALUE: 0, OP: 1 } }, 's블록', (sprite, script) => { const x = Number(script.getValue('VALUE', script)); const op = script.getField('OP', script); const factorial = n => { n = Math.floor(Math.abs(n)); if (n > 170) return Infinity; let r = 1; for (let i = 2; i <= n; i++) r *= i; return r; }; // 감마함수 근사 (Lanczos approximation) const gamma = n => { if (n < 0.5) return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n)); n -= 1; const g = 7, c = [0.99999999999980993,676.5203681218851,-1259.1392167224028,771.32342877765313,-176.61502916214059,12.507343278686905,-0.13857109526572012,9.9843695780195716e-6,1.5056327351493116e-7]; let x2 = c[0]; for (let i = 1; i < g + 2; i++) x2 += c[i] / (n + i); const t = n + g + 0.5; return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x2; }; switch(op) { case 'sin': return Math.sin(x); case 'cos': return Math.cos(x); case 'tan': return Math.tan(x); case 'asin': return Math.asin(x); case 'acos': return Math.acos(x); case 'atan': return Math.atan(x); case 'sinh': return Math.sinh(x); case 'cosh': return Math.cosh(x); case 'tanh': return Math.tanh(x); case 'log': return Math.log(x); case 'log10': return Math.log10(x); case 'log2': return Math.log2(x); case 'exp': return Math.exp(x); case 'factorial': return factorial(x); case 'gamma': return gamma(x); case 'sqrt': return Math.sqrt(x); case 'cbrt': return Math.cbrt(x); case 'abs': return Math.abs(x); case 'ceil': return Math.ceil(x); case 'floor': return Math.floor(x); case 'round': return Math.round(x); case 'sign': return Math.sign(x); default: return 0; } }, 'basic_string_field'); // 두 값 대학수학 연산 (값블록) addBlock('s블록_대학수학2', '%1 과 %2 의 %3', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [ ['최대공약수 GCD', 'gcd'], ['최소공배수 LCM', 'lcm'], ['나머지(mod)', 'mod'], ['정수 나눗셈', 'idiv'], ['조합 C(n,r)', 'comb'], ['순열 P(n,r)', 'perm'], ['arctan2 (각도)', 'atan2'], ['거듭제곱 n^m', 'pow'], ['로그 밑변환 log_m(n)', 'logb'] ], value: 'gcd', fontSize: 11 } ], def: [{ type: 'number', params: ['12'] }, { type: 'number', params: ['8'] }, 'gcd'], map: { A: 0, B: 1, OP: 2 } }, 's블록', (sprite, script) => { const a = Number(script.getValue('A', script)); const b = Number(script.getValue('B', script)); const op = script.getField('OP', script); const gcd = (x, y) => { x = Math.abs(Math.floor(x)); y = Math.abs(Math.floor(y)); while (y) { [x, y] = [y, x % y]; } return x; }; const factorial = n => { n = Math.floor(Math.abs(n)); if (n > 170) return Infinity; let r = 1; for (let i = 2; i <= n; i++) r *= i; return r; }; switch(op) { case 'gcd': return gcd(a, b); case 'lcm': { const g = gcd(a, b); return g === 0 ? 0 : Math.abs(Math.floor(a) * Math.floor(b)) / g; } case 'mod': return ((a % b) + b) % b; case 'idiv': return Math.trunc(a / b); case 'comb': { const n = Math.floor(a), r = Math.floor(b); if (r < 0 || r > n) return 0; return factorial(n) / (factorial(r) * factorial(n - r)); } case 'perm': { const n = Math.floor(a), r = Math.floor(b); if (r < 0 || r > n) return 0; return factorial(n) / factorial(n - r); } case 'atan2': return Math.atan2(a, b); case 'pow': return Math.pow(a, b); case 'logb': return Math.log(a) / Math.log(b); default: return 0; } }, 'basic_string_field'); // 수학 상수 블록 (값블록) addBlock('s블록_수학상수', '%1', { color: '#000000', outerline: '#333333', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [ ['π (파이)', 'PI'], ['e (오일러)', 'E'], ['황금비 φ', 'PHI'], ['√2', 'SQRT2'], ['√3', 'SQRT3'], ['ln(2)', 'LN2'], ['ln(10)', 'LN10'], ['log₂(e)', 'LOG2E'], ['log₁₀(e)', 'LOG10E'], ['오일러-마스케로니 γ', 'EULER_MASCHERONI'], ['카탈란 상수 K', 'CATALAN'] ], value: 'PI', fontSize: 11 }], def: ['PI'], map: { CONST: 0 } }, 's블록', (sprite, script) => { const c = script.getField('CONST', script); switch(c) { case 'PI': return Math.PI; case 'E': return Math.E; case 'PHI': return (1 + Math.sqrt(5)) / 2; case 'SQRT2': return Math.SQRT2; case 'SQRT3': return Math.sqrt(3); case 'LN2': return Math.LN2; case 'LN10': return Math.LN10; case 'LOG2E': return Math.LOG2E; case 'LOG10E': return Math.LOG10E; case 'EULER_MASCHERONI': return 0.5772156649015329; case 'CATALAN': return 0.9159655941772190; default: return 0; } }, 'basic_string_field'); // JS 코드 실행 addBlock('s블록_js코드실행', 'JS 코드 실행하기: %1 %2', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['console.log("안녕!")'] }, null], map: { CODE: 0 } }, 's블록', (sprite, script) => { try { const code = script.getValue('CODE', script); // eslint-disable-next-line no-new-func (new Function(code))(); } catch(e) { console.error('JS 코드 실행 오류:', e); } return script.callReturn(); }); // 마우스락 (자동 클릭 반복) addBlock('s블록_마우스락', '마우스락 %1 %2', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['켜기', 'on'], ['끄기', 'off']], value: 'on', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['on', null], map: { STATE: 0 } }, 's블록', (sprite, script) => { const state = script.getField('STATE', script); if (state === 'on') { if (window.s블록_마우스락_interval) return script.callReturn(); window.s블록_마우스락_interval = setInterval(() => { const el = document.elementFromPoint( window.s블록_마우스락_x !== undefined ? window.s블록_마우스락_x : window.innerWidth / 2, window.s블록_마우스락_y !== undefined ? window.s블록_마우스락_y : window.innerHeight / 2 ); if (el) { el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, clientX: window.s블록_마우스락_x || 0, clientY: window.s블록_마우스락_y || 0 })); el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, clientX: window.s블록_마우스락_x || 0, clientY: window.s블록_마우스락_y || 0 })); el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, clientX: window.s블록_마우스락_x || 0, clientY: window.s블록_마우스락_y || 0 })); } }, 50); if (!window.s블록_마우스락_mousemove) { window.s블록_마우스락_mousemove = (e) => { window.s블록_마우스락_x = e.clientX; window.s블록_마우스락_y = e.clientY; }; window.addEventListener('mousemove', window.s블록_마우스락_mousemove); } } else { if (window.s블록_마우스락_interval) { clearInterval(window.s블록_마우스락_interval); window.s블록_마우스락_interval = null; } if (window.s블록_마우스락_mousemove) { window.removeEventListener('mousemove', window.s블록_마우스락_mousemove); window.s블록_마우스락_mousemove = null; } } return script.callReturn(); }); // 마우스 커서 숨기기/보이기 addBlock('s블록_마우스커서', '마우스 커서 %1 %2', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['숨기기', 'hide'], ['보이기', 'show']], value: 'hide', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['hide', null], map: { STATE: 0 } }, 's블록', (sprite, script) => { const state = script.getField('STATE', script); const canvas = document.querySelector('canvas'); if (state === 'hide') { if (canvas && canvas.requestPointerLock) canvas.requestPointerLock(); } else { if (document.exitPointerLock) document.exitPointerLock(); } return script.callReturn(); }); // 현재 마우스락 상태인가? addBlock('s블록_마우스락상태', '현재 마우스락 상태인가?', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return !!window.s블록_마우스락_interval; }, 'basic_boolean_field'); // 현재 마우스가 숨겨져있나/보이는가? addBlock('s블록_마우스커서상태', '현재 마우스가 %1?', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['숨겨져있나', 'hidden'], ['보이는가', 'visible']], value: 'hidden', fontSize: 11 }], def: ['hidden'], map: { STATE: 0 } }, 's블록', (sprite, script) => { const state = script.getField('STATE', script); const isLocked = !!document.pointerLockElement; return state === 'hidden' ? isLocked : !isLocked; }, 'basic_boolean_field'); // ═══════════════════════════════════════════════ // 요일 계산 addBlock('s블록_요일계산', '%1 년 %2 월 %3 일의 요일', { color: '#1565C0', outerline: '#0D47A1', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }], def: [{ type: 'number', params: ['2024'] }, { type: 'number', params: ['1'] }, { type: 'number', params: ['1'] }], map: { YEAR: 0, MONTH: 1, DAY: 2 } }, 's블록', (sprite, script) => { const date = new Date(Number(script.getValue('YEAR', script)), Number(script.getValue('MONTH', script)) - 1, Number(script.getValue('DAY', script))); return ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'][date.getDay()]; }, 'basic_string_field'); // 간지 계산 addBlock('s블록_간지계산', '%1 년의 간지', { color: '#1565C0', outerline: '#0D47A1', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'number', params: ['2024'] }], map: { YEAR: 0 } }, 's블록', (sprite, script) => { const year = Number(script.getValue('YEAR', script)); const stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계']; const branches = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해']; const diff = year - 2015; return stems[((1 + diff) % 10 + 10) % 10] + branches[((7 + diff) % 12 + 12) % 12]; }, 'basic_string_field'); // 윤년 판단 addBlock('s블록_윤년판단', '%1 년이 윤년인가?', { color: '#1565C0', outerline: '#0D47A1', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'number', params: ['2024'] }], map: { YEAR: 0 } }, 's블록', (sprite, script) => { const year = Number(script.getValue('YEAR', script)); return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }, 'basic_boolean_field'); // ═══════════════════════════════════════════════ // 장면 제어 // ═══════════════════════════════════════════════ // 현재 장면 이름 addBlock('s블록_현재장면이름', '현재 장면 이름', { color: '#1E8449', outerline: '#145A32', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return Entry.scene?.selectedScene?.name || ''; }, 'basic_string_field'); // 장면 개수 addBlock('s블록_장면개수', '현재 사용된 장면의 수', { color: '#1E8449', outerline: '#145A32', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return Entry.scene?.scenes_?.length || 0; }, 'basic_string_field'); // 장면 추가 addBlock('s블록_장면추가', '장면 %1 개 추가하기 %2', { color: '#1E8449', outerline: '#145A32', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'number', params: ['1'] }, null], map: { COUNT: 0 } }, 's블록', (sprite, script) => { const count = Number(script.getValue('COUNT', script)); if (Entry.scene && count > 0) { for (let i = 0; i < count; i++) { const s = Entry.scene.addScene(); s.name = `새 장면 ${Entry.scene.scenes_.length}`; } if (Entry.playground) Entry.playground.reloadPlayground(); } return script.callReturn(); }); // N번째 장면 시작 addBlock('s블록_번째장면시작', '%1 번째 장면 시작하기 %2', { color: '#1E8449', outerline: '#145A32', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'number', params: ['1'] }, null], map: { INDEX: 0 } }, 's블록', (sprite, script) => { const index = Number(script.getValue('INDEX', script)); if (Entry.scene?.scenes_) { const target = Entry.scene.scenes_[index - 1]; if (target) Entry.scene.selectScene(target); else console.warn(`장면 ${index}번은 존재하지 않습니다.`); } return script.callReturn(); }, 'basic_without_next'); // ═══════════════════════════════════════════════ // 변수 / 리스트 조작 // ═══════════════════════════════════════════════ // 변수 만들기 addBlock('s블록_변수만들기', '이름이 %1 이고 기본값이 %2 인 변수 만들기 %3', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['새 변수'] }, { type: 'text', params: ['0'] }, null], map: { NAME: 0, VALUE: 1 } }, 's블록', (sprite, script) => { const name = script.getValue('NAME', script), value = script.getValue('VALUE', script); if (!Entry.variableContainer.variables_.some(v => v.getName() === name)) { Entry.variableContainer.addVariable({ name, value }); if (Entry.playground?.variableView) Entry.playground.variableView.updateView(); } return script.callReturn(); }); // 변수 조작 addBlock('s블록_변수조작', '%1 이름의 변수 %2 %3 %4', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['만큼 더하기', 'add'], ['으로 정하기', 'set']], value: 'add', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['변수'] }, { type: 'number', params: ['10'] }, 'add', null], map: { NAME: 0, VALUE: 1, TYPE: 2 } }, 's블록', (sprite, script) => { const name = script.getValue('NAME', script), value = script.getValue('VALUE', script), type = script.getField('TYPE', script); const variable = Entry.variableContainer.variables_.find(v => v.getName() === name); if (variable) { if (type === 'add') variable.setValue((Number(variable.getValue()) || 0) + (Number(value) || 0)); else variable.setValue(value); if (Entry.playground?.variableView) Entry.playground.variableView.updateView(); } else { console.warn(`변수 "${name}"을(를) 찾을 수 없습니다.`); } return script.callReturn(); }); // 변수/리스트 존재 확인 addBlock('s블록_변수존재확인', '만일 %1 이름의 %2 가 존재하는가?', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['변수', 'variable'], ['리스트', 'list']], value: 'variable', fontSize: 11 }], def: [{ type: 'text', params: ['이름'] }, 'variable'], map: { NAME: 0, TYPE: 1 } }, 's블록', (sprite, script) => { const name = script.getValue('NAME', script), type = script.getField('TYPE', script); if (type === 'variable') return Entry.variableContainer.variables_.some(v => v.getName() === name); else return Entry.variableContainer.lists_.some(l => l.getName() === name); }, 'basic_boolean_field'); // 오브젝트 존재 확인 addBlock('s블록_오브젝트존재확인', '현재 작품에 %1 이름의 오브젝트가 존재하는가?', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }], def: [{ type: 'text', params: ['오브젝트이름'] }], map: { NAME: 0 } }, 's블록', (sprite, script) => { return Entry.container.objects_.some(obj => obj.name === script.getValue('NAME', script)); }, 'basic_boolean_field'); // 변수/리스트 개수 addBlock('s블록_변수개수', '현재 %1 개수', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['변수', 'variable'], ['리스트', 'list']], value: 'variable', fontSize: 11 }], def: ['variable'], map: { TYPE: 0 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); return type === 'variable' ? Entry.variableContainer.variables_.length : Entry.variableContainer.lists_.length; }, 'basic_string_field'); // 리스트 항목 조작 addBlock('s블록_리스트항목조작', '%1 이름의 리스트 %2 번째 항목을 %3 %4 %5', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['만큼 더하기', 'add'], ['으로 정하기', 'set']], value: 'set', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['리스트'] }, { type: 'number', params: ['1'] }, { type: 'number', params: ['10'] }, 'set', null], map: { NAME: 0, INDEX: 1, VALUE: 2, TYPE: 3 } }, 's블록', (sprite, script) => { const name = script.getValue('NAME', script), index = Number(script.getValue('INDEX', script)) - 1; const value = script.getValue('VALUE', script), type = script.getField('TYPE', script); const list = Entry.variableContainer.lists_.find(l => l.getName() === name); if (list) { if (index >= 0 && index < list.array_.length) { if (type === 'add') list.array_[index].data = (Number(list.array_[index].data) || 0) + (Number(value) || 0); else list.array_[index].data = value; list.updateView(); } else { console.warn(`리스트 "${name}"의 ${index + 1}번째 항목이 존재하지 않습니다.`); } } else { console.warn(`리스트 "${name}"을(를) 찾을 수 없습니다.`); } return script.callReturn(); }); // 리스트 크기 조작 addBlock('s블록_리스트크기조작', '%1 이름의 리스트 항목수를 %2 %3 %4', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Dropdown', options: [['으로 정하기', 'set'], ['만큼 더하기', 'add']], value: 'set', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: [{ type: 'text', params: ['리스트'] }, { type: 'number', params: ['10'] }, 'set', null], map: { NAME: 0, VALUE: 1, TYPE: 2 } }, 's블록', (sprite, script) => { const name = script.getValue('NAME', script), value = Number(script.getValue('VALUE', script)), type = script.getField('TYPE', script); const list = Entry.variableContainer.lists_.find(l => l.getName() === name); if (list) { const cur = list.array_.length, target = Math.max(0, type === 'set' ? value : cur + value); if (target > cur) { for (let i = cur; i < target; i++) list.array_.push({ data: '' }); } else { list.array_.splice(target); } list.updateView(); } else { console.warn(`리스트 "${name}"을(를) 찾을 수 없습니다.`); } return script.callReturn(); }); // 모두 삭제 addBlock('s블록_모두삭제', '모든 %1 삭제하기 %2', { color: '#E91E63', outerline: '#C2185B', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['변수', 'variable'], ['리스트', 'list'], ['신호', 'message']], value: 'variable', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['variable', null], map: { TYPE: 0 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); if (type === 'variable') [...Entry.variableContainer.variables_].forEach(v => Entry.variableContainer.removeVariable(v)); else if (type === 'list') [...Entry.variableContainer.lists_].forEach(l => Entry.variableContainer.removeList(l)); else if (type === 'message') [...Entry.variableContainer.messages_].forEach(m => Entry.variableContainer.removeMessage(m)); if (Entry.playground?.variableView) Entry.playground.variableView.updateView(); return script.callReturn(); }); // ═══════════════════════════════════════════════ // 텍스트 // ═══════════════════════════════════════════════ // 줄바꿈 addBlock('s블록_줄바꿈', '줄바꿈', { color: '#7B3F00', outerline: '#5C2E00', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return '\n'; }, 'basic_string_field'); // ═══════════════════════════════════════════════ // 오브젝트 // ═══════════════════════════════════════════════ // 오브젝트 가로 addBlock('s블록_오브젝트가로', '이 오브젝트의 가로', { color: '#0D6EFD', outerline: '#0B5ED7', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return sprite.width || sprite.getWidth?.() || 0; }, 'basic_string_field'); // 오브젝트 세로 addBlock('s블록_오브젝트세로', '이 오브젝트의 세로', { color: '#0D6EFD', outerline: '#0B5ED7', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { return sprite.height || sprite.getHeight?.() || 0; }, 'basic_string_field'); // ═══════════════════════════════════════════════ // 재미 / 특수 효과 // ═══════════════════════════════════════════════ // 붕어빵 addBlock('s블록_붕어빵', '%1 붕어빵 %2', { color: '#4A148C', outerline: '#311B92', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['팥', '팥'], ['슈크림', '슈크림']], value: '팥', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['팥', null], map: { TYPE: 0 } }, 's블록', (sprite, script) => script.callReturn()); // 붕어빵 판매 addBlock('s블록_붕어빵판매', '%1 붕어빵을 %2 원에 팔기 %3', { color: '#4A148C', outerline: '#311B92', fontColor: '#ffffff' }, { params: [{ type: 'Dropdown', options: [['팥', '팥'], ['슈크림', '슈크림']], value: '팥', fontSize: 11 }, { type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 }], def: ['팥', { type: 'number', params: ['1000'] }, null], map: { TYPE: 0, PRICE: 1 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script), price = Number(script.getValue('PRICE', script)); let allVars = Entry.variableContainer.variables_, currentVar = null, currentName = "붕어빵 수익"; for (let i = 0; i < allVars.length; i++) { if (allVars[i].getName().startsWith("붕어빵 수익")) { if (!currentVar || allVars[i].getName().length >= currentVar.getName().length) currentVar = allVars[i]; } } let lastValue = currentVar ? Number(currentVar.getValue()) : 0; if (currentVar) { currentName = currentVar.getName(); Entry.variableContainer.removeVariable(currentVar); } const newName = currentName + " ", newValue = lastValue + price; Entry.variableContainer.addVariable({ name: newName, value: newValue }); if (Entry.playground?.variableView) Entry.playground.variableView.updateView(); alert(`[${type} 붕어빵 판매 완료]\n"${newName}"\n합계: ${newValue}원`); return script.callReturn(); }); // 쀒 10번 addBlock('s블록_쀒', '쀒쀒쀒쀒쀒쀒쀒쀒쀒쀒 %1', { color: '#4A148C', outerline: '#311B92', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { if (!script.isStarted) { script.isStarted = true; script.step = 0; } const messages = ['쀒쀒쀒쀒쀒쀒쀒쀒쀒쀒','쀒쀒쀒쀒쀒쀒쀒쀒쀒','쀒쀒쀒쀒쀒쀒쀒쀒','쀒쀒쀒쀒쀒쀒쀒','쀒쀒쀒쀒쀒쀒','쀒쀒쀒쀒쀒','쀒쀒쀒쀒','쀒쀒쀒','쀒쀒','쀒']; if (script.step < messages.length) { alert(messages[script.step]); script.step++; return script; } delete script.isStarted; delete script.step; return script.callReturn(); }); // 큰 블록 큰 작품 addBlock('s블록_큰블록큰작품', '큰블록 큰작품 %1', { color: '#4A148C', outerline: '#311B92', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 35 }], def: [null], map: {} }, 's블록', (sprite, script) => script.callReturn()); // 계속 가까이 오기 addBlock('s블록_계속가까이오기', '계속 가까이 오기 %1', { color: '#4A148C', outerline: '#311B92', fontColor: '#ffffff' }, { params: [{ type: 'Indicator', img: HW_ICON, size: 11 }], def: [null], map: {} }, 's블록', (sprite, script) => { sprite.setSize(sprite.getSize() + 20); sprite.setRotation(sprite.getRotation() + 20); if (!script.isWaiting) { script.isWaiting = true; script.timeFlag = Date.now(); } if (Date.now() - script.timeFlag < 10) return script; script.isWaiting = false; return script; }); // ═══════════════════════════════════════════════ // 시스템 추가 블록 // ═══════════════════════════════════════════════ // 기기 기종 addBlock('s블록_기기기종', '현재 기기 기종', { color: '#795548', outerline: '#5D4037', fontColor: '#ffffff' }, { params: [], def: [], map: {} }, 's블록', (sprite, script) => { const ua = navigator.userAgent; if (/iPad/.test(ua)) return 'iPad'; if (/iPhone/.test(ua)) return 'iPhone'; if (/Galaxy/.test(ua)) return 'Samsung Galaxy'; if (/Android/.test(ua)) { const m = ua.match(/;\s*([^;)]+)\sBuild\//); return m ? m[1].trim() : 'Android 기기'; } if (/Windows NT/.test(ua)) return 'Windows PC'; if (/Macintosh/.test(ua)) return 'Mac'; if (/Linux/.test(ua)) return 'Linux PC'; return '알 수 없는 기기'; }, 'basic_string_field'); // ═══════════════════════════════════════════════ // 이펙트 // ═══════════════════════════════════════════════ // 이펙트 재생 addBlock('s블록_이펙트재생', '%1 이펙트 재생하기 %2', { color: '#FFCC00', outerline: '#E6B800', fontColor: '#ffffff' }, { params: [ { type: 'Dropdown', options: [ ['띠용띠용', 'bounce'], ['레전드', 'legend'], ['매트릭스', 'matrix'], ['색상반전', 'colorshift'], ['3D회전', '3drotate'], ['모든 이펙트', 'all'] ], value: 'bounce', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 } ], def: ['bounce', null], map: { TYPE: 0 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); const canvas = document.querySelector('canvas'); if (!canvas) return script.callReturn(); switch(type) { case 'bounce': if (!window.s블록_띠용띠용_interval) { canvas.style.transition = "0.1s"; let y = 0, direction = 1; window.s블록_띠용띠용_interval = setInterval(() => { y += 10 * direction; if (y > 50 || y < 0) direction *= -1; canvas.style.transform = `translateY(${-y}px) scaleY(${1 + y/200})`; }, 30); } break; case 'legend': if (!window.s블록_레전드_running) { window.s블록_레전드_running = true; let speedX = 5, speedY = 5, posX = 0, posY = 0; canvas.style.position = 'fixed'; canvas.style.zIndex = '9999'; canvas.style.pointerEvents = 'none'; canvas.style.boxShadow = '0 0 50px rgba(0,255,0,0.5)'; function bounce() { if (!window.s블록_레전드_running) return; posX += speedX; posY += speedY; if (posX + canvas.width > window.innerWidth || posX < 0) speedX *= -1; if (posY + canvas.height > window.innerHeight || posY < 0) speedY *= -1; canvas.style.transform = `translate(${posX}px, ${posY}px) rotateX(${posX}deg) rotateY(${posY}deg)`; requestAnimationFrame(bounce); } bounce(); } break; case 'matrix': if (!window.s블록_매트릭스_interval) { let invert = 0; window.s블록_매트릭스_interval = setInterval(() => { invert = invert === 100 ? 0 : 100; canvas.style.filter = `invert(${invert}%)`; }, 500); } break; case 'colorshift': canvas.style.filter = "hue-rotate(90deg) contrast(150%)"; break; case '3drotate': if (!window.s블록_3drotate_interval) { let angle = 0; window.s블록_3drotate_interval = setInterval(() => { angle += 5; canvas.style.transform = `perspective(500px) rotateY(${angle}deg)`; }, 30); } break; case 'all': if (!window.s블록_띠용띠용_interval) { canvas.style.transition = "0.1s"; let y = 0, dir = 1; window.s블록_띠용띠용_interval = setInterval(() => { y += 10 * dir; if (y > 50 || y < 0) dir *= -1; canvas.style.transform = `translateY(${-y}px) scaleY(${1 + y/200})`; }, 30); } if (!window.s블록_레전드_running) { window.s블록_레전드_running = true; let speedX = 5, speedY = 5, posX = 0, posY = 0; canvas.style.position = 'fixed'; canvas.style.zIndex = '9999'; canvas.style.pointerEvents = 'none'; canvas.style.boxShadow = '0 0 50px rgba(0,255,0,0.5)'; (function bounceAll() { if (!window.s블록_레전드_running) return; posX += speedX; posY += speedY; if (posX + canvas.width > window.innerWidth || posX < 0) speedX *= -1; if (posY + canvas.height > window.innerHeight || posY < 0) speedY *= -1; canvas.style.transform = `translate(${posX}px, ${posY}px) rotateX(${posX}deg) rotateY(${posY}deg)`; requestAnimationFrame(bounceAll); })(); } if (!window.s블록_매트릭스_interval) { let invert = 0; window.s블록_매트릭스_interval = setInterval(() => { invert = invert === 100 ? 0 : 100; canvas.style.filter = `invert(${invert}%)`; }, 500); } canvas.style.filter = "hue-rotate(90deg) contrast(150%)"; if (!window.s블록_3drotate_interval) { let angle = 0; window.s블록_3drotate_interval = setInterval(() => { angle += 5; canvas.style.transform = `perspective(500px) rotateY(${angle}deg)`; }, 30); } break; } return script.callReturn(); }); // 이펙트 중지 addBlock('s블록_이펙트중지', '%1 이펙트 중지하기 %2', { color: '#FFCC00', outerline: '#E6B800', fontColor: '#ffffff' }, { params: [ { type: 'Dropdown', options: [ ['띠용띠용', 'bounce'], ['레전드', 'legend'], ['매트릭스', 'matrix'], ['색상반전', 'colorshift'], ['3D회전', '3drotate'], ['모든 이펙트', 'all'] ], value: 'all', fontSize: 11 }, { type: 'Indicator', img: HW_ICON, size: 11 } ], def: ['all', null], map: { TYPE: 0 } }, 's블록', (sprite, script) => { const type = script.getField('TYPE', script); const canvas = document.querySelector('canvas'); if (!canvas) return script.callReturn(); if (type === 'bounce' || type === 'all') { if (window.s블록_띠용띠용_interval) { clearInterval(window.s블록_띠용띠용_interval); window.s블록_띠용띠용_interval = null; canvas.style.transform = ''; canvas.style.transition = ''; } } if (type === 'legend' || type === 'all') { window.s블록_레전드_running = false; canvas.style.position = ''; canvas.style.zIndex = ''; canvas.style.pointerEvents = ''; canvas.style.boxShadow = ''; canvas.style.transform = ''; } if (type === 'matrix' || type === 'all') { if (window.s블록_매트릭스_interval) { clearInterval(window.s블록_매트릭스_interval); window.s블록_매트릭스_interval = null; canvas.style.filter = ''; } } if (type === 'colorshift' || type === 'all') { canvas.style.filter = ''; } if (type === '3drotate' || type === 'all') { if (window.s블록_3drotate_interval) { clearInterval(window.s블록_3drotate_interval); window.s블록_3drotate_interval = null; canvas.style.transform = ''; } } return script.callReturn(); }); // 화면 흔들기 addBlock('s블록_화면흔들기', '엔트리 실행화면을 %1 초동안 %2 강도로 흔들기 %3', { color: '#FFCC00', outerline: '#E6B800', fontColor: '#ffffff' }, { params: [ { type: 'Block', accept: 'string' }, { type: 'Block', accept: 'string' }, { type: 'Indicator', img: HW_ICON, size: 11 } ], def: [{ type: 'number', params: ['1'] }, { type: 'number', params: ['10'] }, null], map: { SEC: 0, INTENSITY: 1 } }, 's블록', (sprite, script) => { const sec = parseFloat(script.getValue('SEC', script)) || 1; const intensity = parseFloat(script.getValue('INTENSITY', script)) || 10; const canvas = document.querySelector('canvas'); if (!canvas) return script.callReturn(); const duration = sec * 1000; const start = Date.now(); if (window.s블록_흔들기_interval) { clearInterval(window.s블록_흔들기_interval); canvas.style.transform = ''; } window.s블록_흔들기_interval = setInterval(() => { if (Date.now() - start >= duration) { clearInterval(window.s블록_흔들기_interval); window.s블록_흔들기_interval = null; canvas.style.transform = ''; return; } const x = (Math.random() * 2 - 1) * intensity; const y = (Math.random() * 2 - 1) * intensity; canvas.style.transform = `translate(${x}px, ${y}px)`; }, 30); return script.callReturn(); }); // ═══════════════════════════════════════════════ // 블록 목록 등록 // ═══════════════════════════════════════════════ Entry.staticBlocks.push({ category: 's블록', visible: true, blocks: [ // 기본 's블록_title_기본', 's블록_주석', 's블록_메모', 's블록_NaN값', 's블록_참거짓', 's블록_복제본인가', 's블록_조건부기다리기', 's블록_스크롤판단', 's블록_모든블록해제', 's블록_해제하기', 's블록_아이디블록가져오기', // 팝업 / 알림 / 클립보드 's블록_title_팝업', 's블록_경고창', 's블록_선택프롬프트', 's블록_복사하기', 's블록_클립보드값', // 브라우저 / 페이지 's블록_title_브라우저', 's블록_브라우저이름', 's블록_현재링크', 's블록_페이지이름바꾸기', 's블록_페이지새로고침', 's블록_사이트열기', 's블록_엔트리로그아웃', // 콘솔 's블록_title_콘솔', 's블록_콘솔입력', 's블록_콘솔지우기', // 작품 제어 's블록_title_작품제어', 's블록_작품일시정지', 's블록_작품멈추기', 's블록_엔트리콘솔지우기', // 엔트리 페이지 / 검색 's블록_title_엔트리페이지', 's블록_엔트리페이지열기', 's블록_탐험하기열기', 's블록_인작보러가기', 's블록_엔트리검색', 's블록_엔트리검색확장', 's블록_제작자페이지열기', // 컴퓨터 저장소 (localStorage) 's블록_title_저장소', 's블록_localStorage간편저장', 's블록_localStorage간편확인', 's블록_localStorage간편삭제', 's블록_localStorage저장', 's블록_localStorage불러오기', 's블록_localStorage삭제', // iframe 's블록_title_iframe', 's블록_iframe하기', 's블록_iframe해제하기', 's블록_iframe상태인가', // 수학 계산 's블록_title_수학', 's블록_참판단', 's블록_수판단', 's블록_약수개수', 's블록_두점거리', 's블록_진법변환', 's블록_삼항조건식', 's블록_배열항목수', 's블록_대학수학', 's블록_대학수학2', 's블록_수학상수', // 날짜 / 시간 's블록_title_날짜', 's블록_요일계산', 's블록_간지계산', 's블록_윤년판단', // 장면 제어 's블록_title_장면', 's블록_현재장면이름', 's블록_장면개수', 's블록_장면추가', 's블록_번째장면시작', // 변수 / 리스트 's블록_title_변수', 's블록_변수만들기', 's블록_변수조작', 's블록_변수존재확인', 's블록_오브젝트존재확인', 's블록_변수개수', 's블록_리스트항목조작', 's블록_리스트크기조작', 's블록_모두삭제', // 이펙트 's블록_title_이펙트', 's블록_이펙트재생', 's블록_이펙트중지', 's블록_화면흔들기', // 텍스트 ← 새로 추가 's블록_title_텍스트', 's블록_줄바꿈', // 오브젝트 's블록_title_오브젝트', 's블록_오브젝트가로', 's블록_오브젝트세로', // 재미 's블록_title_재미', 's블록_붕어빵', 's블록_붕어빵판매', 's블록_쀒', 's블록_큰블록큰작품', 's블록_계속가까이오기', // 시스템 's블록_title_시스템', 's블록_기기기종', 's블록_js코드실행', 's블록_마우스락', 's블록_마우스커서', 's블록_마우스락상태', 's블록_마우스커서상태' ] }); updateCategory('s블록'); $('head').append(``); if ($('#entryCategorys블록').length) $('#entryCategorys블록').text('s블록'); console.log('%c┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ ┃', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ %c제작자: ywsa_%c ┃', 'color: #1E8449; font-weight: bold;', 'color: #FFD700; font-size: 16px; font-weight: bold;', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ ┃', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ %cs블록 로딩이 완료되었습니다!%c ┃', 'color: #1E8449; font-weight: bold;', 'color: #00FF00; font-size: 14px; font-weight: bold;', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ ┃', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ %c버전: v1.4.1%c ┃', 'color: #1E8449; font-weight: bold;', 'color: #3498DB; font-size: 13px; font-weight: bold;', 'color: #1E8449; font-weight: bold;'); console.log('%c┃ ┃', 'color: #1E8449; font-weight: bold;'); console.log('%c┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛', 'color: #1E8449; font-weight: bold;');