3333#"""3"""3"""3"""3"""3"""#"""3333""""""""""""""""""""""""""""3333"""""""2"""2"""2"""2"""2"""2#"""3"""3"""3"""3"""3"""#"""3333""""""""""""""""""""""""""""3333"""2"""2"""2"""2"""2"""2""""3333Р?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџЬџЬџџЯќЯЯЯЯџќЯЯџЬџџЯЬџќџџЯЬџќЯЯџЬџџџЬЯќЯќџЬЯЯџќџЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯЯќЯЯџџџЬЯќЯќЯЯЯЯЯќџџџќЯЯџџЯЬЯЯЯЯЯЯџџЯџџЬЯЯЯќЯЯџќЯЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџЯЯќџџЯЯЯџЯџЯЯЯЬџЯџџџќЯЯџџЯЯЯЯЯЯЯќџџЯџЯЯЯЯџЯЯЯџќЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬЯќџЬџџЯЬЯџЯџџќЯЬЯќџџџЯџќџџЯЯџќџќџЬџџџЬЯЬЯќЯќџЬЯЬџЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџџџџџџЯЯџќџЬЯќџџџќџџџџџџџќџџџџџџџџџџџџџџџџџџЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџЬџЬџџЯЯЯЯЯџЯЯџџЯЬџќџџџЬЯЬџЬџЬЯЯџџџЬЯќЯќџЬЯЯџќџЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯЯќЯЯџџЯЯЯЬџќЯЯџџџќЯЯџџЯќџќЯЯЯЯЯќџџЯџџЬЯЯЯќЯЯџќЯЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЯџЯЯќџџЯЬЯЯџЯЯЯџџџќЯЯџџџЯџќЯќЯќЯџџџЯџЯЯЯЯџЯЯЯџќЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬЯќџЬџџЯЬЯЯЯќЯќџџџЯџќџџЯќџЯџЬџЬЯџџџџЬЯЬЯќЯќџЬЯЬџЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬЬЬЬЬЬЬЬЬЬќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџьююююўџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬЬЬЬЬЬЬЬЬЬќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюџџџџџџџюўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўўяџяџўўџўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўўўўўўюџяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўўўўўўўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюџяџяџўџяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюџџџџџяџџџџџџџюџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўўяўяўюўюџюџяўяџяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюўўўўџяџяўўўўўяџўўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюџюџўџяџўўўўюўяџюџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўўяўяўџўюўўўџўюўяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяўяџџџџџюџџџџџџџџџџџџџџџџџџяџџџџџџџџџџюџџџџџџџџяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўџџџўўяўяџяўџџџџџџџџџџџџџџџџџџџџџџџџџяџяўяўџюяяџўџюџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўџяџюџўџяџўўџџџџџџџџџџџџџџџџџџџџџџџџџяџџюяяяўяяџўяяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўџяџўџўџяџюџџџџџџџџџџџџџџџџџџџџџџџџџџяџяяяяџяяяџўяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяўяџўџяўюўяўџџџџџџџџџџџџџџЯќџџџџџџџџџџюяюяўяўџюяюџюџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќЯџџџџџџџџџџџџџяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЬЬЬџџџџџџџџџџџџџџџџџЯџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯќџџџЬџџџџџџџџџџџџџџџџќџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџќџџџџџџџџџџџџџџЬџџџџџЯџџџџџџџџџџџЯЬЬЬЬЬЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџЯџџџџџџџџџџџџџЯџџџџџџџќџџџџџџџџџЬќџџџџџџЬЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџќџџџџџџџџџџџџќџџџџџџџЯџџџџџџџџџќџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџЯџџџџџџџџџџџЯџџџџџџџџџќџџџџџџџЯџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџЯџџџџџџџџџџџќџџџџџџџџџЯџџџџџџџќџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџџџџЯџџџџџџџџџџџќџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџџџџќџџџџџџџџџџЬџџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџџџЬџџџџџџџџџџЯџџџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџьююююќџџџџџџџЯџџџџџяќџџџџќџџџџџџџЯџџџџџџьюююююќџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџџќџџџџџўџџџџЯџџџџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџЯџџџџџюџџџџџќџџџџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџќЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџќџџџџџџџќџџџяџџџџџЯџџџџџџџџџџќџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџЯџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџЯџџџџџџџџЯџџџўџџџџџќџџџџџџџџџџЯџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџЯџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџЯџџџџџџџџџќџяџџџџџЬџџџџџџџџџџџЯџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџќџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџќџџџџџџџџџЯџўџџџџЯџџџџџџџџџџџџџЬќџџџџџџЯЬџџџџџџџџџџџџџџџџџџџџџџЯџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџЯџџџџџџџџџџџьџџџџџќџџџџџџџџџџџџџџЯЬЬЬЬЬЬќџџџџџџџџџџџџџџџџџџџџџџџЯџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџќџџџџџџџџџџџќџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯќџџџЬџџџџџџџџџџџџЯџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЬЬЬџџџџџџџџџџџџџџќџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџьюююююьюююўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќџџџџџџџџџџџџџџџџџџЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬЬќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяўџџяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяяџўџўяяџюџўяўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяўяяџўяяяяяяяяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяџяяџўџюяюяяяяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяџџўяюџяџяџўяяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўџўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџх3-- title: Collision Tests -- author: TylerDurden -- desc: An attempt at SAT collision detection and basic (and bugged) collision response -- script: lua ---------------------- -- 2D Vector library vector={}vector.__index=vector;setmetatable(vector,{__call=function(a,...)return a.new(...)end})function vector.new(b,c)local self=setmetatable({},vector)self.x=b or 0;self.y=c or 0;return self end;function vector:__tostring()return string.format("(%s,%s)->%s",self.x,self.y,self:mag())end;function vector.isvector(d)return getmetatable(d)==vector end;function vector.lerp(e,f,g)local function h(i,j,k)return(1-k)*i+k*j end;return vector(h(e.x,f.x,g),h(e.y,f.y,g))end;function vector.fromAngle(l)return vector(math.cos(l),-math.sin(l))end;function vector.random()return vector.fromAngle(math.random()*math.pi*2)end;function vector:set(b,c)self.x=b;self.y=c end;function vector:__add(d)assert(vector.isvector(d),'Wrong type, expected a vector')return vector(self.x+d.x,self.y+d.y)end;function vector:__sub(d)assert(vector.isvector(d),'Wrong type, expected a vector')return vector(self.x-d.x,self.y-d.y)end;function vector:__mul(d)if type(d)=='number'then return vector(self.x*d,self.y*d)end;assert(vector.isvector(d),'Wrong type, expected a vector or a number')return vector(self.x*d.x,self.y*d.y)end;function vector:__div(d)assert(type(d)=='number','Wrong type, expected a number')return vector(self.x/d,self.y/d)end;function vector:__eq(d)assert(vector.isvector(d),'Wrong type, expected a vector')return self.x==d.x and self.y==d.y end;function vector:__unm()return vector(-self.x,-self.y)end;function vector:dist(d)assert(vector.isvector(d),'Wrong type, expected a vector')local m=self.x-d.x;local n=self.y-d.y;return math.sqrt(m*m+n*n)end;function vector:normal()return vector(-self.y,self.x)end;function vector:dot(d)assert(vector.isvector(d),'Wrong type, expected a vector')return self.x*d.x+self.y*d.y end;function vector.cross(e,f)return e.x*f.y-e.y*f.x end;function vector:replace(d)assert(vector.isvector(d),'Wrong type, expected a vector')self.x=d.x;self.y=d.y end;function vector:clone()return vector(self.x,self.y)end;function vector:mag(o)if o then assert(type(o)=='number','Wrong type, expected a number')return self:normalize()*o else return math.sqrt(self.x*self.x+self.y*self.y)end end;function vector:magSq()return self.x*self.x+self.y*self.y end;function vector:normalize()local p=self:mag()local q=self:clone()if p~=0 then q=q/p end;return q end;function vector:limit(r)assert(type(r),'Wrong type, expected a number')local p=self:mag()local d=self:clone()if p>r*r then d=d:mag(r)end;return d end;function vector:clamp(s,r)assert(vector.isvector(s)and vector.isvector(r),'Wrong type, expected a number')local b=math.min(math.max(self.x,s.x),r.x)local c=math.min(math.max(self.y,s.y),r.y)return vector(b,c)end;function vector:heading()local l=math.atan2(self.y,-self.x)return math.pi-l end;function vector:head(l)local o=self:mag()local d=vector.fromAngle(l)d=d:mag(o)return d end;function vector:rotate(l)local t=math.sin(l)local a=math.cos(l)local d=vector(a*self.x+t*self.y,-(t*self.x)+a*self.y)return d end;function vector:rotateAround(u,l)local v=vector(math.cos(-l),math.sin(-l))local w=self-u;local d=vector(w.x*v.x-w.y*v.y,w.x*v.y+w.y*v.x)return d+u end;function vector.project(d,x)local y=vector()y.x=(d.x*x.x+d.y*x.y)/(x.x*x.x+x.y*x.y)*x.x;y.y=(d.x*x.x+d.y*x.y)/(x.x*x.x+x.y*x.y)*x.y;return y end;function vector:array()return{self.x,self.y}end;function vector:unpack(d)return self.x,self.y end ---------------------- ---------------------- -- Input Helper input={codes={a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16,q=17,r=18,s=19,t=20,u=21,v=22,w=23,x=24,y=25,z=26,[0]=27,[1]=28,[2]=29,[3]=30,[4]=31,[5]=32,[6]=33,[7]=34,[8]=35,[9]=36,minus=37,equals=38,leftbracket=39,grave=44,rightbracket=40,backslash=41,semicolon=42,apostrophe=43,comma=45,period=46,slash=47,space=48,tab=49,['return']=50,backspace=51,delete=52,insert=53,pageup=54,pagedown=55,home=56,['end']=57,up=58,down=59,left=60,right=61,capslock=62,ctrl=63,shift=64,alt=65,mouse1='mouse1',mouse2='mouse2',mouse3='mouse3',scrollup='scrollup',scrolldown='scrolldown'},_presses={},presses={},x=0,y=0,binds={}}function input:bind(a,b)b=self.codes[b]self.binds[b]=self.binds[b]or{}table.insert(self.binds[b],a)end;function input:update()self._presses=self.presses;self.presses={}for c=0,3 do local d=peek(0xff88+c)if d>0 then self.presses[d]=true end end;local e,f,g,h,i,j,k=mouse()self.x=e;self.y=f;self.presses['mouse1']=g;self.presses['mouse2']=i;self.presses['mouse3']=h;self.presses['scrollup']=k>0;self.presses['scrolldown']=k<0;for l,j in pairs(self.presses)do if self.binds[l]then for j,a in pairs(self.binds[l])do self.presses[a]=self.presses[l]end end end end;function input:pressed(b)return(self.presses[self.codes[b]]or self.presses[b])and not(self._presses[self.codes[b]]or self._presses[b])end;function input:down(b)return self.presses[self.codes[b]]or self.presses[b]end;function input:released(b)return not(self.presses[self.codes[b]]or self.presses[b])and(self._presses[self.codes[b]]or self._presses[b])end ---------------------- ---------------------- -- SAT(Separating Axis Theorem) library sat={}function sat.overlap(a,b,c,d)local e=math.min(b,d)local f=math.max(a,c)local g=e-f;return math.max(0,g)end;function sat.get_nearest_vertex(h,i)local j=vector.dist(h,i[1])local k=0;local l=1;for m=2,#i do k=vector.dist(h,i[m])if kA then A=E;D=z.vertices[m]end end;return j,A,C,D end;function sat.check(F,G)if F.type=='polygon'then if G.type=='polygon'then return sat.polygon_polygon(F,G)else return sat.circle_polygon(F,G)end elseif G.type=='polygon'then return sat.circle_polygon(F,G)else return sat.circle_circle(F,G)end end;function sat.circle_circle(F,G)local o=F.pos-G.pos;local k=o:mag()local H=F.radius+G.radius;if k=b2.x and b1.y<=b2.y+b2.h and b1.y+b1.h>=b2.y end function newShape(x,y) local s={} s.pos=vector(x,y) s.vel=vector() s.rot=0 s.angVel=0 s.static=false s.radius=0 s._vertices={} s.vertices={} function s:updateVertices() if self.type=='polygon' then self.vertices={} for i=1,#self._vertices do self.vertices[i]=self._vertices[i]:rotateAround(self.pos,self.rot) end end end function s:getPosition() return self.pos:clone() end function s:setPosition(x,y) local v=vector(x,y)-self:getPosition() self:move(v.x,v.y) end function s:move(x,y) self.pos=self.pos+vector(x,y) if self.type=='polygon' then for i=1,#self.vertices do self._vertices[i]=self._vertices[i]+vector(x,y) end end self:updateVertices() end function s:getRotation() return self.rot end function s:getDirection() return vector.fromAngle(self.rot) end function s:setRotation(r) self.rot=r end function s:rotate(a) self.rot=self.rot-a if self.rot>math.pi*2 then self.rot=0 end if self.rot<0 then self.rot=math.pi*2 end self:updateVertices() end function s:getBB() if self.type=='circle' then return {x=self.pos.x-self.radius,y=self.pos.y-self.radius,w=self.radius*2+1,h=self.radius*2+1} else local min=vector(math.huge,math.huge) local max=vector(-math.huge,-math.huge) for i=1,#self.vertices do local v1=self.vertices[i] if v1.x<=min.x then min.x=v1.x end if v1.y<=min.y then min.y=v1.y end if v1.x>=max.x then max.x=v1.x end if v1.y>=max.y then max.y=v1.y end end return {x=min.x,y=min.y,w=max.x-min.x+1,h=max.y-min.y+1} end end function s:debug() if key(16) then local bb=self:getBB() rectb(bb.x,bb.y,bb.w,bb.h,9) end local dir=self:getDirection()*(self.type=='circle' and self.radius or self.width/2) line(self.pos.x,self.pos.y,self.pos.x+dir.x,self.pos.y+dir.y,14) pix(self.pos.x,self.pos.y,12) if self.type=='circle' then circb(self.pos.x,self.pos.y,self.radius,12) else for i=1,#self.vertices do local v1=self.vertices[i] local v2=self.vertices[i+1==#self.vertices+1 and 1 or i+1] line(v1.x,v1.y,v2.x,v2.y,12) end end end return s end function newCircle(x,y,r) local c=newShape(x,y) c.type='circle' c.radius=r return c end function newRectangle(x,y,w,h) local r=newShape(x,y) r.type='polygon' r.width=w r.height=h r._vertices[1]=vector(x-w/2,y-h/2) r._vertices[2]=vector(x+w/2,y-h/2) r._vertices[3]=vector(x+w/2,y+h/2) r._vertices[4]=vector(x-w/2,y+h/2) r.vertices[1]=vector(x-w/2,y-h/2) r.vertices[2]=vector(x+w/2,y-h/2) r.vertices[3]=vector(x+w/2,y+h/2) r.vertices[4]=vector(x-w/2,y+h/2) return r end function newCapsule(x,y,l,r) local c=newShape(x,y) c.type='polygon' l=math.max(l-r,r/2) r=r/2 c.width=l+r*2 c.height=r/2 local i=1 for a=10,0,-2 do local x=x+l/2 local dx=math.cos(math.pi/2-a/math.pi)*r+x local dy=math.sin(math.pi/2-a/math.pi)*r+y c._vertices[i]=vector(dx,dy) c.vertices[i]=vector(dx,dy) i=i+1 end for a=0,10,2 do local x=x-l/2 local dx=math.cos(math.pi/2+a/math.pi)*r+x local dy=math.sin(math.pi/2+a/math.pi)*r+y c._vertices[i]=vector(dx,dy) c.vertices[i]=vector(dx,dy) i=i+1 end return c end function newPolygon(x,y,vs) local p=newShape(x,y) p.type='polygon' for i,v in pairs(vs) do p._vertices[i]=vector(v[1],v[2]) p.vertices[i]=vector(v[1],v[2]) end local bb=p:getBB() p.width=bb.w p.height=bb.h return p end circle=newCircle(30,68,10) rectangle=newRectangle(70,68,25,15) capsule=newCapsule(110,68,25,15) polygon=newPolygon(170,86,{{170,68},{150,98},{190,98}}) door=newPolygon(150,31,{{150,30},{170,30},{170,32},{150,32}}) --door.static=true function TIC() cls(15) door:setPosition(150,31) rectangle:rotate(0.01) local v=vector() local s,t=1.6,0.09 if input:down('up') then v.y=v.y-s end if input:down('down') then v.y=v.y+s end if input:down('left') then v.x=v.x-s end if input:down('right') then v.x=v.x+s end if input:down('a') then capsule:rotate(-t) end if input:down('d') then capsule:rotate(t) end if input:down('w') then local dir=capsule:getDirection() v=dir:mag(s) end if input:down('s') then local dir=capsule:getDirection() v=-dir:mag(s) end capsule:move(v.x,v.y) local shapes={circle,rectangle,polygon,door} for i,sh in pairs(shapes) do local b1,b2=capsule:getBB(),sh:getBB() if checkAABB(b1,b2) then local coll,c,n,p=sat.check(capsule,sh) if coll then local d1=n*p local d2=-n*p capsule:move(d1.x,d1.y) if not sh.static then sh:move(d2.x,d2.y) local tp=c-sh.pos local rt=vector.cross(tp,d2) local mt=math.min(.01,1/tp:magSq()) circb(c.x,c.y,2,2) sh:rotate(rt*mt) end end end end print('Circle',20,50,14,1,1,1) print('Rectangle',50,45,14,1,1,1) print('Capsule',95,50,14,1,1,1) print('Door?',150,35,14,1,1,1) print('Polygon',155,105,14,1,1,1) circle:debug() rectangle:debug() capsule:debug() polygon:debug() door:debug() print('Use arrows to move capsule',1,1,12,1,1,1) print('Use WASD to steer capsule',1,8,12,1,1,1) input:update() end