0,]']±>Sï}WÿÍu§ðp8·d%qy)6o;]ÉA¦ösï÷ôôô”°ÂVl†33DDDD333CDDDD3333333CDDD4333DDDD433333333DDîîþìîîþ©š©ªªšÿ"""Ì""""""""Ýÿ""""""Ý-bgÿ"""Ì""""""""Òmwfgfgfvgfvff§šª©ª8333CD43333DD43DDDDDD33CDDDDD33333CDDDDD4333333DDD¨¨š¨ˆª©š©ª©š©"ÿÿ""""""""ÝÝ""îîîîîî""rw"ÿÿ""""""""ÒÝ-r‡šªvwfwgfwgf¨ª©ªªªªšª˜¨:CDDDD43CDDDDD433DDDDDD4333CDDDDDDD333333DDDªª‰ªŠª¨ª˜©Š¨š"ÿÿ""""""""ÝÝ""îîîîîî""’Š"ÿÿ""""""""ÒÝ-r§‰ªšªªªªªªŠ©ªª¨¨¨ªªªˆš‰ªŠ©˜ªªªªˆŠ©ªJDD3CDDDDDDD333DDDDDDDD33333DDDD¨ª‰©¨ªˆ©ªªªªŠ"""îîîîîîîî"""""""""""Ò�ˆ"""îîîîîîîî.""¢ªš¨ª¨ªªªªªŠªŠªª©ªªªªªª˜ªš™¨ªˆ©ª¨ªš‰ªšªš©ª™©š©ª©J3DDDDDDDDDD333DDDDD™ªˆ¨š™ªª©ªª™ª"""îîîîîîîî"""/"""""""Ý­ˆ"""îîîîîîîî.""¢ªªš©ªš¨ªšªªªªªªªªŠ¨ªªš¨ŠˆªªªªŠ‰š‰ªªˆ©Š©ªªªªŠŠª©ªªªˆªªªªªªªª:3CDDDDD™©ª¨¨ª™©ªš©ªªÿ""""""""""""Ý/"""""""Ý�©ÿ""""""""""""Ò­ªˆ™ªªªªªªš©ªš˜ªª™ªªªªªªªªªˆªªªª¨ŠŠªªªªªˆˆª™¨ªŠ˜©š©ªªªªª˜¨š©ªªª‰ªªªªˆªªŠ©ŠŠª™ªª™©ÿ""""""""""""ÝÿîîîîîîÝ-’©ÿ""""""""""""Ò­ª™¨ªŠªªˆªªªªªªªªŠŠªš©ªªŠ™ª˜©ªªªªªª™ªš‰ªªª™ªˆŠªªªªªªªª‰ªª¨ªª˜ªªªªªªªªªªªªªš‰ªªªªªÿÿ""""""""""ÝÝòîîîîîîÝ"¢ªÿÿ""""""""""ÒÝ�Šªªªªªªªªª‰ªªŠªªš™ªªªªªªªªªª™ªª˜™‰ªªš¨™ªª¨ªªš™ˆªª™ª©š©ªª™™ªª™¨ªš©ªª©ªªªªªªªªšª˜ªÿÿ""""""""""ÝÝòîîîîîîÝ"¢ªÿÿ""""""""""ÒÝ­ˆ©ŠˆˆªŠ˜ªªŠ™ª¨ªª™ªªš™ªªˆ‰ªª™™ªªª™ª©ˆ©ªªˆ™©ªªªªªª©˜©ªªš™©ªª™©ªªªªªªªªªªªªšª™©ªª™™"ÿÿîîîîîîîîÝÝ"""""""""Ò­ˆ"ÿÿîîîîîîîîÞÝ-’™¨ªªªˆ™ªŠ™ªªªªªªªª˜ˆªš™ªªªªš‰ªˆŠªªªªªªªªªªšˆªªŠ¨ªªš™ªªªªªªªªªªªš©ªªªª‰¨™ªªªªªªªš"ÿÿîîîîîîîîÝÝ"/"""""""Ý�©"ÿÿîîîîîîîîÞÝ-¢ªŠ¨ˆªšˆªªš‰ªªš™ªªª™™ªªª™©ªªš™ªªª™™©ªª¨Šªªš™ˆ¨ªªŠ˜™‰¨ªª™©ªªªªªªªªªªªªªªªªªªªªŠˆˆª""ÿîîîîîîîîÝ""/"""""""Ý�ª""ÿîîîîîîîîÞ-"¢ªªªªªªªªªšˆªªªˆªªªš™ªªªªªªªªªªªªªš™ªªª™™™¨ªªš‰¨™ªªªªªªªª™™ˆªªš™ªªª™Š™¨ªªš™©ªªª™©""ÿîîîîîîîîÝ""ÿîîîîîîÝ-¢ª""ÿîîîîîîîîÞ-"¢ªªªªªªªªš™©ªªªˆ¨ªªª™©ªªªªªªªªªªªªªŠªŠˆ©ªªªªª™©ªŠˆˆ¨ªªªªªªªªŠ™¨ª™‰ˆªªªª™‰ªšªªªªªªÿ""""""""""""ÝòîîîîîîÝ"¢ª""ÿîîîîîîîîÞ-"¢ªªªª™¨ªªªšˆªªŠˆˆˆªŠˆªªªšˆªªª˜‰ªªªš™©ªªª™™ªªªªš™ªªªš™™©ªŠˆŠ¨ªª™™‰ˆªªªˆ™¨ªªªªªªªªªÿ""""""""""""ÝòîîîîîîÝ"¢ªÿ""""""""""""Ò­ˆªªªª˜™ªªªªªªªªªªªªªªªª™©ªªª˜™™‰ªªªª™ˆª™ªªªªªªªªªš™™ˆªªªš‰ªšªš™©ªªª©ªªªªªªªªªªªªÿÿ""""""""""ÝÝ""""""""Ò­ªÿ""""""""""""Ò­ªªª™‰ªªˆˆˆ¨ªªªªªªªªªª˜™¨ªš™‰ˆªªªªŠ˜™©ªŠ˜‰¨ªªªªªªªªªªˆ¨ˆªªˆ™ªªªªªªª‰ªŠˆ‰¨ªªª˜™ªªªÿÿ""""""""""ÝÝÿ""""""Ý�ˆˆÿÿ""""""""""ÒÝ�Šˆ¨ªªªŠˆ¨™ªªªªªªªªªªªªªª™™ªªªªš‰˜©ªªªš™©ªªªªªªªªªªªªªªªªªªšˆ¨ªªªªªª™Šˆ¨ªªªªˆ˜™™©"ÿÿîîîîîîîîÝÝ"ÿ""""""Ý­ªšÿÿ""""""""""ÒÝ�¨ªªŠˆ©ªªªŠˆ¨ªªˆ˜‰¨ªªªˆ™ªªªªŠ™™ªªªªªª™™ªªªª™™™©ªªŠˆ‰ˆ¨ªš™™™‰¨ªªªª‰ˆšªªˆ˜©ªªªªªªªª"ÿÿîîîîîîîîÝÝ"ªîîîîîªª"ÿÿîîîîîîîîÞÝ-¢ªªªª™™Šˆ©ªªªšˆˆš™Šˆ™ªªªªŠ˜™™™©ªªˆˆˆˆªªª™™™©ªªªª™™ªªªªªªš™‰ªªªªš™ˆªª™ªªŠ™‰¨ªªªªªª""ÿîîîîîîîîÝ""™îîîîî™‰"ÿÿîîîîîîîîÞÝ-¢ªªªªªªªªª‰ˆ¨ªªªªˆªªªªª˜™©ªªªªªªªªªªªªªªªªªªªª™™ªªªªš™™™™¨ªªªªš‰ˆª™©ªªªªªªªªªªªªª""ÿîîîîîîîîÝ""ªªªªªªªª™ˆª""ÿîîîîîîîîÞ-"¢ªªªŠˆ™ˆ¨ªª™™ˆˆªªªªªˆˆ˜™©ªª˜™‰¨ªªªªªªªªªªªªŠˆˆˆˆªªˆ™©ªªªªªªªª™ˆˆªªªª™™™ªªªªªªªªªªÿ""""""""""""Ý™™™™‰ªªªªš™""ÿîîîîîîîîÞ-"¢ªªªªªªªª™™ˆªªªªªˆ¨ªªªªˆˆ©ªªªªªªªªªªªªªªªªªªªˆ¨Šˆ¨ªª™™©ªªªªªªª˜™™©ªª¨ªªªª˜™™ªªªªªÿ""""""""""""ݪªªª™™©ªªªªÿ""""""""""""Ò­ªª™™™‰ˆˆªªªªªŠˆ˜™™™ˆ¨ªªªªš™ˆªª©ªªªªªªªªªªªªªªªªªªš™™ªªªªªªˆˆ¨ªªªªŠ™™ªªªªªªªš™™©ªªÿÿ""""""""ÝÝ™ˆˆªªªˆˆ˜ªªªÿ""""""""""""Ò­ªª˜™©ªªªªªš™™©ªªªªªªª™™©ªªªªš™™™™©ªªŠˆˆŠˆ¨ªªª™™™™‰ˆªªªªªšˆˆ¨™™‰ˆˆªªˆ™™™ˆˆ˜ªªªªªªˆÿÿ""""""""Ý݈ˆªªª™™™™™©ªªÿÿ""""""""ÒÝ�™™©ªªªªªš™™¨ªªªªŠ˜™‰¨ªªªŠˆ˜©ªªªªªˆˆ¨ªªŠˆˆ‰ˆ¨ªªªŠˆ™©ªªªªªŠ™™©ªªªªªªªªˆ˜ªªªªªªš™™©ªªªªîîîîîîîªªªªªª™ªªˆ™™ªÿÿ""""""""ÒÝ­ªªªªªªˆˆ™™™™ªªªªŠˆªˆˆªªªª™™™™™©ªªªªª™™©ªªªªªªª™™™ªªªªªª™™™ªªªªªª˜™‰¨ªªªª™ˆˆªªªªªªªªªîîîîîîîªªªªªªªªªªš™™¨ªîîîîîîîªªªª™™™ªªªªªªªªªªªªªªªªªªªªªš©ªŠ˜™©ªªªªªˆˆ™™™™ˆˆ¨ªªªªªªˆˆ˜™™™©ªªªªˆ¨ªˆˆ™™™ªªªˆ˜™ˆ¨ªªªîîîîîîîªªªˆˆˆŠˆˆªªªˆ™™îîîîîîîª‰ˆˆ¨ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™©ªªªªªšˆˆ˜ªªªªªªš™™™ªªªªªªªªªªªª™‰¨ªªŠˆˆˆˆˆ¨ªªªªªªªªªªªªªªªªªˆ™™ˆªªª™™™‰ˆˆ¨ªªªªªªˆˆ˜™™©ªªŠˆ˜‰ˆˆªªªªªªªªªªªªªªªªªˆˆªˆˆªªªŠˆ™©ªªªªªªªªªªªŠˆˆ˜ªªªªªªªªªªªªªˆ™™ªªªªªªªªªªªªªªªªªªªªªªªªªˆªªˆ¨ªªªˆ˜™©ªªªªªªªªªª™™™©ªªªˆˆ˜‰ˆˆªªªªªªªªªªªªªªªªªªš™™™ªªªš™™™ˆˆ¨ªªªªªªªŠˆ™™™ªªªˆˆˆ‰ˆˆ¨ªªª™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™‰¨ªªªªªªŠˆ¨ªªªªªªˆˆ˜ªªªªªªªªªªªªªªªªªªªªªªªªªªˆˆªŠˆ¨ªªªŠ™™™ªªªªªªªªªª˜™™™©ªªªˆ˜™‰ˆªªªˆˆˆ˜™™©ªªªªªªªªªªªªªªªªªªª™ªªŠ™™™ªªªªªªªš™™‰˜™™ªªªªªªš™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª™‰ˆ¨ªªªªªªª¨ªªªªªª‰ˆˆ¨ªªŠ¨ªªˆ¨ªš™™™™™™ªªªªˆˆˆªˆˆ¨ªªªªš™™™™‰ˆˆªªªªªªªªˆˆˆ˜™™™‰ˆˆ¨ªªªªªª™™ˆ¨ªš™ªªªªªªªªªªªªªªªªªªªªªªªªªš™™™ªªªªªªªš™ˆˆ˜©ªªªªªª™™™©ªªªªªªš™™‰ªªªªªªª˜™™‰¨ªªªªªš™™™™™ªªªªªªªª™™™™ªªªªªªªš™™™ªªªªªªªˆ˜™™™™™ªªªªªªˆªªª¨ªªªªªªª™™™ªˆˆ˜©ªªªªªªªŠˆ˜™™™™™‰¨ªªªªªªš™™™ªªªŠˆˆˆ©ªªªˆˆˆ˜ˆˆˆªªªªªˆˆ˜™ªªªªªªªªˆ˜™™ªªªª¨ªªªªªªª™™™©ªªªªªªš™™™™™ªªªªªŠˆˆ™ˆˆˆªªªªš™™™™™™‰¨ªªªªªªªš™‰ˆ¨ª™™©Šˆˆ˜©ªªªªªªŠˆˆˆª™™™©ªªªªªªªˆªªªª™™‰ˆªªªªªªªª™‰ˆˆ¨ªªªªˆˆˆˆˆˆˆ¨ªªª™ˆˆˆªªªªªªªš™ˆˆªªªªªªŠ˜™™‰ªªªªªªªš™™™™ªªªªªªªª™™™™ªªªªªªªªªš™™™©ªªªªªªŠª™™™™ªªªªªªªªš™™™™ªªªªªªªªš™™™™©ªªªªªªª™™™™¨ªªªªªªŠ˜™™©ªªªªªªªˆˆ˜™ªªªŠˆˆˆŠˆˆˆªªªªˆˆˆˆ©ªªªªªªªªˆˆ˜™ªªªªŠˆªªŠ¨ªªªš™™™¨ªªªªªšˆˆˆªš™™ªŠˆˆ™™ªªªªªªªªŠ˜™™™™™™™ªªªªŠˆˆˆ˜ˆˆˆªªªªªš™™™™™™ªªªªªªªš™™™©ªªªªªªªªªªªš™™™ˆªªªªªªªªš™™ˆˆªªªªªŠˆˆ˜‰ˆˆˆªªªªŠˆˆˆ¨ªªŠˆ™™™™™™‰ˆˆªªªªªªªªš‰ˆˆˆš™™™©ªªªªªªªŠ¨ªªŠˆªªªªªªš™™™™™™ˆ¨ªªªªªªªš™™™©ªªªªªªªª™™™™©ªªªªªªªªš™™™™™©ªªªªªªŠ˜™™™¨ªªªªªªªŠ™™ªš™™ˆˆ™™™ªªªªªªªªª™™™™¨ªªª™ªªªªªªªªªªªªªªªªªªªªªªªš™™™ªˆˆˆ˜ªªªªªªªª‰ˆˆˆª™™™©Šˆˆ˜™ªªªªªªªªªŠ˜™™™™™™™©ªªªªˆˆˆˆ‰ˆˆˆªªªªªš™™ªªˆˆˆ¨ªªªªªªªªª™™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª˜™™™©ªªªªªªªªˆ˜™™™™™™ˆˆˆªªªªªªªªª‰ˆˆˆ¨™™™™©ªªªªªªªŠ¨ªªŠˆªªªªªªª™ªª¨ªªªªªªªªªˆˆˆ™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™™™ªªªªªªªªš™™‰˜™™™©ªªªªªªªªª™™™™¨ªªª™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™ˆˆˆªªªªªˆªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª™™™‰¨ªªªªªªªªˆˆˆˆ˜ªªªªªªªªªš™™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªŠ™™™™©ªªªªŠˆˆˆ¨ˆˆˆˆªªªªªªªªªªªªªªªªªªªªªªªªªª¨ªªªªªªªªŠˆˆˆˆªªªªªªªªªˆ¨ªªªªªªªªªŠˆ™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªŠˆˆˆ˜™™™™ªªªªŠˆ˜™™ˆˆ¨ªªªªªªªªªªªªªªªªªªªªªªªªªˆˆˆˆªˆˆˆ¨ªªªªˆˆ™™©ªªªªªªªªªªªªªªªªª‰ˆˆˆˆ©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªˆˆˆ™™™™™ªªªªªˆ™™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªŠˆˆ™™ˆˆˆ¨ªªªªš™™™™‰ªªªªªªªªªªªªªªª™™™‰ˆªªªªªŠˆˆªªŠˆ¨ªªªªªªªªªªªªªªªªªªªªªª™™™™™©ªªªªŠˆˆˆ˜™ˆˆˆªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™™™©ªªªªª™™™™™ˆˆˆ¨ªªªªªªªªªªªªˆ˜™™™™©ªªªªˆˆˆˆ˜ˆˆˆˆ¨ªªªªªªªªªªªªªªªªªªªªš™‰ˆˆªªªªªˆˆˆ¨ªˆˆˆˆªªªªªªªªªªªªªªªªªªªªªªªªªªŠˆˆ˜™™ˆˆ¨ªªªªš™™™™ˆˆˆˆªªªªªªªªªªŠˆˆˆˆ˜™™™™©ªªªªˆ˜™™™ˆˆªªªªªªªªªªªªªªªªªªªªªŠˆˆˆˆ¨ªªªªŠªªªªŠ¨ªªªªªªªªªªªªªªªªªªªªªªªªªªªªˆˆˆˆªˆˆˆˆ¨ªªªªš™™™™‰¨ªªªªªªªªªªªªªŠˆˆ™™™™™©ªªªªªˆ™™™™ˆªªªªªªªªªªªªªªªªªªªªªªˆˆ™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªˆ¨ªªªˆˆªªªªªŠˆˆ™™™ªªªªªªªªªªªªªªªªª™™™™™©ªªªªªˆˆˆˆ™‰ˆˆˆªªªªªªªªªªªªªªªªªªªª™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªˆˆˆˆˆ¨ªªªªªªªªªªªªªªªªªªªš™™‰ˆˆªªªªªˆˆˆ¨ªˆˆˆˆ¨ªªªªªªªªªªªªªªªªªª™™™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™‰ˆˆ¨ªªªªªªªªªªªˆªªªªªªªªªªŠˆˆˆˆ¨ªªªªªˆªªªªˆ¨ªªªªªªªªªªªªªªªªªªªš™™™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª™™™™™‰ªªªªªªªªªªªŠˆˆˆ¨ªªªªªªªªªªˆˆˆ™™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™‰ˆˆªªªš™©ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™™™™©ªªªªªªªªªªªš‰ˆˆˆˆ˜ªªªªªªªªªªª˜™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªª‰ˆˆˆˆ¨š™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©ªªªªª™™™™™™ªªªªªªªªªªªª™™™™ˆˆ™™™©ªªªªªªªªªªš™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªˆˆˆˆ™™™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªš™™©ªªŠˆˆ™™™©ªªªªªªªªªªªªˆ™™™™™™™™™™™ªªªªªªªªªªª™™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªŠˆ™™™™™™™™™©ªªªªªªªªªˆˆªªªªˆ¨ªªªªªªªªªªš™™™™™©ˆˆˆˆˆ™ªªªªªªªªªªªªŠˆˆˆ™™™™™™™™™‰ˆ¨ªªªªªªªªªªª™™™™™¨ªªªªªªªªªªªªªªªªªªªªªªªš™™™™™™™™™™™ªªªªªªªˆˆˆˆ¨ªŠˆˆˆªªªªªªªªª™™™™™™™™‰ˆˆˆˆªªªªªªªªªªªªšˆˆˆˆˆ˜™™™™™™‰ˆˆˆˆªªªªªªªªªªªš™™‰ˆˆ¨ªªª™™ªªªªªªªªªªªªªªªªš™™™™™™™™™©ªªªªªªªˆˆˆˆˆ˜ˆˆˆˆˆˆªªªªªªš™™™™™™™™™™‰ˆ¨ªªªªªªªªªªªªš™™ˆˆˆˆªª™™™™©Šˆˆˆˆˆ©ªªªªªªªªªªªŠˆˆˆˆˆª™™™™™ªªªªªªªªªªªªªªª™™™™™™™©ªªªªªªªªªŠˆˆ˜™™‰ˆˆˆªªªªªªªªš™™™™™™™™™™™ªªªªªªªªªªªªª™™™™™‰¨ªªªª™™©ªªªˆˆˆ™™™ªªªªªªªªªªªªˆˆˆˆ˜™™™™™™™ªªªªªªªªªªªªª™™™™™‰ªªªªªªªªªªªªˆ™™™™™ˆ¨ªªªªªªªªªªš™™™™™™™™©ªªªªªªªªªªªªª™™™™™™©ªªªªªªªªªªªªŠ™™™™™©ªªªªªªªªªªªŠˆˆ™™™™™™™™™©ªªªªªªªªªªªš™™‰ˆˆ¨ªªªªªªªªªªˆ˜™™™™™ˆªªªªªªªªªªªª™™™™™™™ªªªªªªªªªªªªªš™™™™™™©ªªªªªªªªªªªªªªª™™™™™™ªªªªªªªªªªªªš™™™™™™™™™™™©ªªªªªªªªªš‰ˆˆˆˆ¨ªªªªªªªªˆˆˆˆ™™™ˆˆˆ¨ªªªªªªªªªªˆˆ™™™™©ªªªªªªªªªªªªªš™™™™™™©ªªªªªªªªªªªªªªªªªš™™™™™©ªªªªªªªªªªªª™™™™™™™™™™™©ªª`1-- title: Raycast Engine Demo -- author: Wojciech Graj -- desc: Demo of a WolfenStein3D-style raycast engine with variable-height walls. -- script: lua -- input: gamepad Player = { pos_x = 0, pos_y = 0, dir_x = -1, dir_y = 0, plane_x = 0, plane_y = 0.8, } Player.__index = Player function Player.new(pos_x, pos_y) local self = setmetatable({}, Player) self.pos_x = pos_x self.pos_y = pos_y return self end function Player:process(delta) if btn(2) then self:rotate(delta) elseif btn(3) then self:rotate(-delta) end if btn(0) then self:move(delta) elseif btn(1) then self:move(-delta) end end function Player:rotate(delta) local speed = 0.001 * delta --[CONST] local old_dir_x = self.dir_x local math_cos = math.cos local math_sin = math.sin self.dir_x = self.dir_x * math_cos(speed) - self.dir_y * math_sin(speed) self.dir_y = old_dir_x * math_sin(speed) + self.dir_y * math_cos(speed) local old_plane_x = self.plane_x self.plane_x = self.plane_x * math_cos(speed) - self.plane_y * math_sin(speed) self.plane_y = old_plane_x * math_sin(speed) + self.plane_y * math_cos(speed) end function Player:move(delta) local speed = 0.003 * delta --[CONST] local math_floor = math.floor if mget(math_floor(self.pos_x + self.dir_x * speed), math_floor(self.pos_y)) == 0 then self.pos_x = self.pos_x + self.dir_x * speed end if mget(math_floor(self.pos_x), math_floor(self.pos_y + self.dir_y * speed)) == 0 then self.pos_y = self.pos_y + self.dir_y * speed end end Sprite = { pos_x = 0, pos_y = 0, tex_id = 0, scl_horiz = 1, scl_vert = 1, offset_vert = 0, --from 0.5 (floor) to -0.5 (ceiling) screen_offset_vert = 0, dist = 0, --Distance to player (negative if not in viewing triangle) screen_x = 0, screen_width = 0, screen_height = 0, draw_start_y = 0, draw_end_y = 0, draw_start_x = 0, draw_end_x = 0, } Sprite.__index = Sprite function Sprite.new(pos_x, pos_y, tex_id, scl_horiz, scl_vert, offset_vert) local self = setmetatable({}, Sprite) self.pos_x = pos_x self.pos_y = pos_y self.tex_id = tex_id self.scl_horiz = scl_horiz self.scl_vert = scl_vert self.offset_vert = offset_vert return self end function Sprite:process(inv_det) local screen_width = g_SCREEN_WIDTH local screen_height = g_SCREEN_HEIGHT local screen_half_height = screen_height / 2 local player = g_player local math_abs = math.abs local rel_x = self.pos_x - player.pos_x local rel_y = self.pos_y - player.pos_y self.dist = math.sqrt(rel_x * rel_x + rel_y * rel_y) local trans_y = inv_det * (player.plane_x * rel_y - player.plane_y * rel_x) if trans_y <= 0 then self.dist = -self.dist return end local trans_x = inv_det * (player.dir_y * rel_x - player.dir_x * rel_y) self.screen_x = math.floor((screen_width * 0.5) * (1 + trans_x / trans_y)) self.screen_width = math_abs(screen_height // trans_y) // self.scl_horiz self.draw_start_x = self.screen_x - self.screen_width // 2 if self.draw_start_x < 0 then self.draw_start_x = 0 elseif self.draw_start_x >= screen_width then self.dist = -self.dist return end self.draw_end_x = self.screen_x + self.screen_width // 2 if self.draw_end_x >= screen_width then self.draw_end_x = screen_width - 1 elseif self.draw_end_x < 0 then self.dist = -self.dist return end self.screen_height = math_abs(screen_height // trans_y) // self.scl_vert self.screen_offset_vert = screen_half_height * self.offset_vert // trans_y self.draw_start_y = screen_half_height - self.screen_height // 2 + self.screen_offset_vert if self.draw_start_y < 0 then self.draw_start_y = 0 end self.draw_end_y = screen_half_height + self.screen_height // 2 + self.screen_offset_vert if self.draw_end_y >= screen_height then self.draw_end_y = screen_height - 1 end end function get_tex_pixel(offset, id, x, y) return peek4(offset + 0x40 * (id + 16 * (y // 8) + x // 8) + 0x8 * (y % 8) + (x % 8)) end function init() g_SCREEN_WIDTH = 240 g_SCREEN_HEIGHT = 136 g_DEBUG = true g_TEX_WIDTH = 16 g_TEX_HEIGHT = 16 g_SPRITE_SIZES = { [0]={16,16}, [2]={16,16}, } g_TEX_MAP = { [1]=1, [2]=3, [3]=5, [4]=7, [5]=9, } g_player = Player.new(22, 12) g_prev_time = 0 g_sprites = { Sprite.new(12, 13, 0, 2, 2, 0.5), Sprite.new(12.5, 12.5, 0, 2, 2, 0.5), Sprite.new(13, 13, 0, 2, 2, 0.5), Sprite.new(18.5, 6.5, 2, 2, 1, 0.125), } g_settings = { floor_ceil = true, interlace = 2, --disabled=g_interlace>=2 } end init() function TIC() local t = time() local delta = t - g_prev_time --msec since last frame g_prev_time = t local screen_width = g_SCREEN_WIDTH local screen_height = g_SCREEN_HEIGHT local screen_half_height = screen_height // 2 local tex_width = g_TEX_WIDTH local tex_height = g_TEX_HEIGHT local sprite_sizes = g_SPRITE_SIZES local tex_map = g_TEX_MAP local player = g_player local sprites = g_sprites local settings = g_settings local math_floor = math.floor local math_abs = math.abs local start_vline local step_vline if settings.interlace >= 2 then start_vline = 0 step_vline = 1 cls(0) else start_vline = (settings.interlace + 1) % 2 settings.interlace = start_vline step_vline = 2 for x=start_vline,screen_width-1,step_vline do for y=0,screen_height-1 do pix(x, y, 0) end end end -- game logic t = time() player:process(delta) if btnp(4) then if settings.interlace >= 2 then settings.interlace = 0 else settings.interlace = 2 end end if btnp(5) then settings.floor_ceil = not settings.floor_ceil end local t_temp = time() local t_logic = t_temp - t t = t_temp -- drawing local inv_det = 1 / (player.plane_x * player.dir_y - player.dir_x * player.plane_y) local visible_sprites = {} for key,sprite in pairs(sprites) do sprite:process(inv_det) if sprite.dist > 0 then visible_sprites[#visible_sprites+1] = sprite end end table.sort(visible_sprites, function(a,b) return a.dist < b.dist end) local num_visible_sprites = #visible_sprites -- draw walls and sprites for x=start_vline,screen_width-1,step_vline do local camera_x = 2 * x / screen_width - 1 local ray_dir_x = player.dir_x + player.plane_x * camera_x local ray_dir_y = player.dir_y + player.plane_y * camera_x local map_x = math_floor(player.pos_x) local map_y = math_floor(player.pos_y) local delta_dist_x = math_abs(1 / ray_dir_x) local delta_dist_y = math_abs(1 / ray_dir_y) local step_x local side_dist_x if ray_dir_x < 0 then step_x = -1 side_dist_x = (player.pos_x - map_x) * delta_dist_x else step_x = 1 side_dist_x = (map_x + 1.0 - player.pos_x) * delta_dist_x end local step_y local side_dist_y if ray_dir_y < 0 then step_y = -1 side_dist_y = (player.pos_y - map_y) * delta_dist_y else step_y = 1 side_dist_y = (map_y + 1.0 - player.pos_y) * delta_dist_y end local current_sprite = 1 local not_hit_full_wall = true local prev_draw_start = screen_height while not_hit_full_wall do -- Get next wall tile using DDA local side local tile_data local not_hit = true while not_hit do if side_dist_x < side_dist_y then side_dist_x = side_dist_x + delta_dist_x map_x = map_x + step_x side = 0 else side_dist_y = side_dist_y + delta_dist_y map_y = map_y + step_y side = 1 end tile_data = mget(map_x, map_y) if tile_data > 0 then not_hit = false end end local perp_wall_dist if side == 0 then perp_wall_dist = (map_x - player.pos_x + (1 - step_x) * 0.5) / ray_dir_x else perp_wall_dist = (map_y - player.pos_y + (1 - step_y) * 0.5) / ray_dir_y end --draw sprites for sprite_idx=current_sprite,num_visible_sprites do local sprite = visible_sprites[sprite_idx] if sprite.dist >= perp_wall_dist then break end current_sprite = sprite_idx + 1 if x >= sprite.draw_start_x and x <= sprite.draw_end_x then local sprite_size = sprite_sizes[sprite.tex_id] local a = sprite_size[2] / sprite.screen_height local sprite_tex_x = math_floor((x - (sprite.screen_x - sprite.screen_width / 2)) * sprite_size[1] / sprite.screen_width) % sprite_size[1] for y=sprite.draw_start_y,sprite.draw_end_y do local tex_y = math_floor((y - sprite.screen_offset_vert - screen_half_height + sprite.screen_height / 2) * a) % sprite_size[2] local color = get_tex_pixel(0xC000, sprite.tex_id, sprite_tex_x, tex_y) if color > 0 and pix(x, y) == 0 then pix(x, y, color) end end end end --draw wall local tile_height = tile_data // 16 / 16 -- 0=full height, 1=no height if tile_height == 0 then not_hit_full_wall = false end local line_height = screen_height // perp_wall_dist local draw_start = screen_half_height + math_floor(line_height * (tile_height - 0.5)) + 1 if draw_start < 0 then draw_start = 0 elseif draw_start >= screen_height then draw_start = screen_height - 1 end local draw_end = screen_half_height + line_height // 2 - 1 if draw_end > prev_draw_start then draw_end = prev_draw_start elseif draw_end >= screen_height then draw_end = screen_height - 1 end local wall_x if side == 0 then wall_x = player.pos_y + perp_wall_dist * ray_dir_y else wall_x = player.pos_x + perp_wall_dist * ray_dir_x end wall_x = wall_x - math_floor(wall_x) local tex_id = tex_map[tile_data % 16] local tex_x = math_floor(wall_x * tex_width) local step_tex = tex_height / line_height local testart_vline = (draw_start - screen_half_height + line_height * 0.5) * step_tex for y=draw_start,draw_end do if pix(x, y) == 0 then local tex_y = math_floor(testart_vline + step_tex * (y - draw_start)) % tex_height pix(x, y, get_tex_pixel(0x8000, tex_id, tex_x, tex_y)) end end --draw top of variable-height walls if tile_height > 0.5 then if side_dist_x < side_dist_y then perp_wall_dist = (map_x + step_x - player.pos_x + (1 - step_x) * 0.5) / ray_dir_x else perp_wall_dist = (map_y + step_y - player.pos_y + (1 - step_y) * 0.5) / ray_dir_y end line_height = screen_height // perp_wall_dist local top_draw_start = screen_half_height + math_floor(line_height * (tile_height - 0.5)) if top_draw_start >= screen_height then top_draw_start = screen_height - 1 end local row_distance_part = (2 * tile_height - 1) * screen_half_height for y=top_draw_start,draw_start - 1 do if pix(x, y) == 0 then local row_distance = row_distance_part / (y - screen_half_height) local floor_x = player.pos_x + row_distance * ray_dir_x local floor_y = player.pos_y + row_distance * ray_dir_y local tex_x = math_floor(tex_width * floor_x) % tex_width local tex_y = math_floor(tex_height * floor_y) % tex_height pix(x, y, get_tex_pixel(0x8000, tex_id, tex_x, tex_y)) end end prev_draw_start = top_draw_start else prev_draw_start = draw_start end end end t_temp = time() local t_wall_sprite = t_temp - t t = t_temp --draw floor + ceiling if settings.floor_ceil then local ray_dir_x0 = player.dir_x - player.plane_x local ray_dir_y0 = player.dir_y - player.plane_y local ray_dir_x1 = player.dir_x + player.plane_x local ray_dir_y1 = player.dir_y + player.plane_y for y=screen_half_height,screen_height do local row_distance = screen_half_height / (y - screen_half_height) local floor_step_x = row_distance * (ray_dir_x1 - ray_dir_x0) / screen_width local floor_step_y = row_distance * (ray_dir_y1 - ray_dir_y0) / screen_width local floor_x = player.pos_x + row_distance * ray_dir_x0 + start_vline * floor_step_x local floor_y = player.pos_y + row_distance * ray_dir_y0 + start_vline * floor_step_y floor_step_x = floor_step_x * step_vline floor_step_y = floor_step_y * step_vline for x=start_vline,screen_width-1,step_vline do local tex_x = math_floor(tex_width * floor_x) % tex_width local tex_y = math_floor(tex_height * floor_y) % tex_height -- draw floor if pix(x, y) == 0 then pix(x, y, get_tex_pixel(0x8000, 3, tex_x, tex_y)) --[CONST] end --draw ceiling if pix(x, screen_height - y - 1) == 0 then pix(x, screen_height - y - 1, get_tex_pixel(0x8000, 5, tex_x, tex_y)) --[CONST] end floor_x = floor_x + floor_step_x floor_y = floor_y + floor_step_y end end end t_temp = time() local t_floor = t_temp - t if g_DEBUG then print(string.format("FPS %d\n#SPR %d\nLOGIC %.1f\nWALL&SPR %.1f\nFLR&CEIL %.1f", math_floor(1000 / delta), num_visible_sprites, t_logic, t_wall_sprite, t_floor), 0, 0, 5) end end