# 달리기

#### **1) 파일**

{% file src="/files/cmgvOgOGGkHUkTRg1FSW" %}

#### **2) main.js**

```jsx
const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;
const STATE_INTRO = 3006;

let _players = App.players; 
let _state = STATE_INIT; 
let _start = false; 
let _stateTimer = 0; 
let _countDown = 10; 
let _finishCountDown = false;
let _finishCount = 30;
let _finishTimer = 0; 
let _delayTimer = 0; 
let _playerBaseSpeed = 120; 
let _rank = 1; 
let _rankList = []; 

//모든 플레이어가 결승점을 통과한 경우 처리  
function finishCheck(){
	
	let allPlayer = 0; 
	let finishedPlayer = 0; 

	for(let p of _players){

		if(!p)
			continue; 

		if(p.tag.isNotPlayer)
			continue; 

		if(p.tag.isFinish)
			finishedPlayer++;
		
		allPlayer++; 
	}

	if(finishedPlayer == allPlayer)
		return true 
	
	return false; 
}

//달리기 맵 설정이 정상적으로 되어있는 지 확인하고, 아닌 경우 에러메세지를 리턴한다. 
function checkSetting(){

	let warningMsg = '';

	if (!Map.hasLocation("race_start_point")) {
		warningMsg = 'race_start_point';

	}
	if (!Map.hasLocation("race_end_point")) {
		warningMsg = 'race_end_point';

	}
	if (!Map.hasLocation("race_finish_point")) {
		warningMsg = 'race_finish_point';

	}
	
	return warningMsg; 
}

function startState(state){

	_state = state; 
  	_stateTimer = 0; 

  	switch (state) {
		
    	case STATE_INIT:
     		for(let p of _players){
				//달리기 앱을 실행한 유저를 확인한다.
				if(p.id == App.creatorID){
					if(p.isMobile)
					//유저의 환경이 모바일인 경우의 위젯 
						p.tag.widget = p.showWidget("setting.html", "bottom", 440, 340);
					else
					//유저의 환경이 pc인 경우의 위젯
						p.tag.widget = p.showWidget("setting.html", "middle", 440, 340);
					
					p.tag.widget.sendMessage({ 
						str_title         : 'Run',
						str_title_text1   : "A race to see who reaches the finish line the fastest",
						str_title_text2   : 'Click the Start Running button to start the game',
						str_title_start   : "Start",
						str_title_how     : "How to set up a map"
					});
					
					p.tag.widget.onMessage.Add(function (sender, msg) {
						//게임을 취소하는 경우, 나타난 위젯을 제거한다 
						if (msg.type == "cancle") {
							if(p.tag.widget_warning){
								p.tag.widget_warning.destroy();
								p.tag.widget_warning = null;
							}

							if(p.tag.widget){
								p.tag.widget.destroy();
								p.tag.widget = null;
							}
						
						} else {
							// 맵에 필요한 설정 체크							
							let warning = checkSetting();
		
							if(warning !== ''){
								if(p.tag.widget_warning){
									p.tag.widget_warning.destroy();
									p.tag.widget_warning = null;
								}
		
								if(p.isMobile)
									p.tag.widget_warning = App.showWidget("warning.html", "top", 440, 60);
								else 
									p.tag.widget_warning = App.showWidget("warning.html", "bottom", 440, 250);
								
								p.tag.widget_warning.sendMessage({
									str_warningText :  'Race track needs to be setup',
									str_warningText2 : ' needed)',
									str_warningText3 : 'Run',
									warningMsg: warning,
								})
							} else {
								if(p.tag.widget){
									p.tag.widget.destroy();
									p.tag.widget = null;
								}
								
								startState(STATE_INTRO);
								_start = true;
							}
						}
					})  
				}
			}
    		break;

		case STATE_INTRO: 
			App.showCenterLabel("\n minigame - RUN \n\n", 0xffffff, 0x000000, 120); ;
			break; 
		
		case STATE_READY:
			for(let p of _players){
				if(p.tag.start)
				   continue; 
				
				//모든 플레이어가 "race_start_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_start_point"); 

				p.moveSpeed = 0; 
			   	p.sendUpdated();
			}
			break;
		
		case STATE_PLAYING:	
			
			App.showCenterLabel("Start!!", 0xffffff, 0x000000, 120); ;
			
			for(let p of _players){
				if(p.tag.start)
				continue; 
				p.moveSpeed = _playerBaseSpeed; 
				p.sendUpdated();
			}
			break

		// 게임 결과 처리 
		case STATE_JUDGE:
			for(let i =0 ; i <_rankList.length; i++){
				let rank = ''; 
				if(i == 0)
					rank = '1st'; 
				else if(i == 1)
					rank = '2nd';
				else if(i == 2)
					rank = '3rd'; 
				else
					rank = `${i + 1}th`;
				//게임종료 후 등수 표시 
				App.sayToAll(`${rank} : ${_rankList[i].name} !`);
			}
			for(let p of _players){
				p.moveSpeed = 80; 
				p.sendUpdated();
			}
			_start = false;

			break

		// 게임 종료 처리 
		case STATE_END:
			for(let p of _players){
				//게임이 종료되면 모든 플레이어는 "race_end_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_end_point"); 
				
				if(p.tag.isNotPlayer)
					p.tag.isNotPlayer = false;
			}
			_start = false;
			
			break
  }
}

App.onLeavePlayer.add(function(p){
	_players = App.players;

	p.moveSpeed = 80; 
	p.sendUpdated();

})

// 플레이어가 해당 맵에 들어왔을 때 처리
App.onJoinPlayer.Add(function (p) {
	p.tag = {};
  
	//게임시작 후 들어온 플레이어 처리 
	if (_start) {
		p.tag.isNotPlayer = true;  

	} else {
		p.tag.isNotPlayer = false; 
		p.tag.speedTimer = 0;
		p.tag.isFinish = false;  
		p.tag.speedChnage = false; 
	  	p.sendUpdated();
	}
	//게임이 시작된 후 들어온 플레이어는 'race_end_point' 로케이션으로 이동시킨다 
	if(p.tag.isNotPlayer)
		p.spawnAtLocation("race_end_point"); 

	_players = App.players;
  });

App.onStart.Add(function(){
	startState(_state);

	//"speed_set_40", "speed_set_60", "speed_set_140", "speed_set_160" 으로 지정한 로케이션을 밟으면 
	//2초간 해당 로케이션 이름의 숫자와 같이 플레이어의 속도가 변한다. (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	//ex) "speed_set_40"를 밟으면 속도가 40으로 변함 
	let speed = [40,60,140,160]; 

	for(let i =0; i<speed.length; i++){
		App.addOnLocationTouched(`speed_set_${speed[i]}`, function(p){

			if(_state !== STATE_PLAYING)
				return; 

			p.moveSpeed = speed[i]; 
			p.tag.speedTimer = 0; 
			p.tag.speedChnage = true;

			//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
			let str = '';
			if(i == 0)
				str = 'Speed greatly reduced';
			else if(i == 1)
				str = 'Speed reduced';
			else if(i == 2)
				str = 'Speed increased';
			else
				str = 'Speed greatly increased';

			p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
			p.sendUpdated();	
		})
	}

	//"speed_set_random" 로케이션을 밟는 경우 플레이어의 속도가 40, 60, 140, 160 중 하나의 속도로 2초간 바뀐다 (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	App.addOnLocationTouched(`speed_set_random`, function(p){
		if(_state !== STATE_PLAYING)
			return; 

		p.moveSpeed = speed[Math.floor(Math.random() * 4)]; 
		//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
		let str = '';
		if(p.moveSpeed == 40)
			str = 'Speed greatly reduced';
		else if(p.moveSpeed == 60)
			str = 'Speed reduced';
		else if(p.moveSpeed == 140)
			str = 'Speed increased';
		else
			str = 'Speed greatly increased';

		p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
		p.tag.speedTimer = 0; 
		p.tag.speedChnage = true;
		p.sendUpdated();
	})

	//플레이어가 결승선을 통과하는 경우
	App.addOnLocationTouched("race_finish_point", function(p){
		if(p.tag.isFinish)
			return; 

		if(p.tag.isNotPlayer)
			return;

		_delayTimer = 0; 

		// 처음으로 플레이어가 결승선을 통과하는 경우 레이스 종료 카운트 다운을 시작한다(디폴트 30초)
		if(!_finishCountDown)
			_finishCountDown = true;

		// 플레이어가 결승선을 최초에 통과한 경우를 체크한다 
		p.tag.isFinish = true; 
		_rankList.push(p); 
		_rank++;
	})
});
 
App.onUpdate.Add(function(dt){
	_stateTimer += dt; 

	if(_start){
		for(let p of _players){
			if(p.tag.isNotPlayer)
				p.showCenterLabel("game is in progress, please wait..",0xffffff, 0x000000, 120);
		}
	}

	switch (_state) {
		case STATE_INIT:
		break;
		case STATE_INTRO: 
			if(_stateTimer >= 3){
				startState(STATE_READY);
			}
			break; 
		case STATE_READY:
			//출발선에서 _countDown 초(디폴트 10초) 간 레이스 카운트 다운을 시작한다 
			App.showCenterLabel(`${_countDown}  seconds later the race will start. `,0xffffff, 0x000000, 120); 

			if(_stateTimer >= 1){
				_stateTimer = 0; 
				_countDown --; 
			} 
			
			//카운트가 종료되면 (_countDown 가 0이 되면) 레이스가 시작된다  
			if(_countDown <= 0)
				startState(STATE_PLAYING); 
			break;
		case STATE_PLAYING:
			
			for(let p of _players){

				if(finishCheck() && _rankList.length == 0){
					p.showCenterLabel("There is no winner",0xffffff, 0x000000, 120);
					p.spawnAtLocation("race_end_point"); 
					startState(STATE_END);
				}
				// else if(finishCheck())
				// 	startState(STATE_JUDGE);
				
				if(p.tag.isNotPlayer)
					continue; 
				if(p.tag.speedChnage)
					p.tag.speedTimer += dt; 
				
				//어떤 속도 지정 타일을 밟아도 2초 후 기본 속도(_playerBaseSpeed)로 돌아온다 
				if(p.tag.speedTimer >= 2 && p.tag.speedChnage){
					p.moveSpeed = _playerBaseSpeed; 
					p.tag.speedChnage = false; 
					p.sendUpdated(); 
				}
			}
			
			if(_finishCountDown)
				{	
					_delayTimer += dt; 
					//플레이어가 결승선을 통과하면 1.5초간 플레이어의 이름과 레이스 카운트다운을 라벨로 나타낸다 
					if(_delayTimer <=1.5)
						App.showCenterLabel(`${_rankList[_rankList.length -1].name}  ${_rank - 1} place ! \n\n ${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);
					else
						App.showCenterLabel(`${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);

					_finishTimer += dt; 

					if(_finishTimer >= 1){
						_finishCount--; 
						_finishTimer = 0; 
					}
					// 레이스 카운트다운이 종료(0초)되었거나, 모든 플레이어가 결승선을 통과한 경우, 다음 단계로 넘어간다  
					if(_finishCount == 0 || finishCheck()){
						startState(STATE_JUDGE); 
					}
				}
			break; 
		case STATE_JUDGE:
			//5초간 우승자(1등)의 이름을 라벨로 나타낸다 
			if(_stateTimer <= 5){
				App.showCenterLabel(`- Winner - \n\n ${_rankList[0].name}`,0xffffff, 0x000000, 120);
			} 
			//5초간 "잠시 후 대기실로 이동합니다"라는 라벨을 띄웁다 
			else if(_stateTimer > 5 && _stateTimer <= 10 ){
				App.showCenterLabel("You will soon be transported to the waiting room",0xffffff, 0x000000, 120);
			}
			else {
				startState(STATE_END);
			}
			break;
		case STATE_END:
			
			break;
	}
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs-kr.zep.us/creator/tutor/sample/running.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
