ruin_lite9_eps.py

Created by mathieu-croslacoste

Created on September 16, 2025

9.73 KB

Update of Ruin by fime ( https://my.numworks.com/python/fime/ruin ).

Added verical wall check that can be enabled/disabled for each level. The last levels are way better on N0120 since they’re full of content.


from math import sqrt,copysign,sin
from kandinsky import fill_rect as F,draw_string as D,color as I
from ion import keydown as K
from time import monotonic as M,sleep as S
from random import randint as R,seed as A
from micropython import const as C
TARG_SPF=C(.022)
BG,PL,PTF,NMY,NMY2=C((75,40,40)),C((240,10,10)),C((200,100,100)),C((0,150,255)),C((0,50,200))
P=1
class Entity():
 def __init__(it,x,y,w,h,dFunc):it.x,it.y,it.w,it.h=x,y,w,h;it.draw=lambda:dFunc(it)
 def hitBox(it,it2):
  if it.x-it2.w<it2.x<it.x+it.w and it.y-it2.h<it2.y<it.y+it.h:return 1
  return 0
class Platform(Entity):
 def __init__(it,x,y,w,h):super().__init__(x,y,w,h,drawPlatform)
 def hitBox(it,it2):
  if super().hitBox(it2) and it2.y+it2.h<it.y+5:return 1
  return 0
class Movable(Entity):
 def __init__(it,*arg):super().__init__(*arg);it.vx,it.vy,it.grounded=0,0,0
 def setVel(it,vx,vy):it.vx,it.vy=vx,vy
 def addVel(it,vx,vy):it.vy+=vy;it.vx+=vx
 def applyPhysics(it):
  if it.grounded:
   it2=it.grounded;it.vy=0
   if it.x>it2.x+it2.w or it2.x>it.x+it.w:it.grounded=0
  else:
   if it==player and ghost[1]and it.vy<0 and it.touchWall(it.vy,0):it.vy=0
   else:it.addVel(0,.2);it.y+=min(it.vy,4)
  if it==player and ghost[0]and it.touchWall(it.vx,1):it.vx=-(it.vx<0)/9999
  it.vx/=1.3;it.x+=it.vx;it.x=max(min(it.x,320-it.w),0)
 def onPlatform(it,plat):
  if plat.hitBox(it)and not it.grounded and it.vy>0:it.grounded=plat;it.vy,it.y=0,plat.y-it.h
 def jump(it):it.vy,it.grounded=-4.3,0
 def touchWall(it,vxy,xOrY):
  for plat in platforms:
   if xOrY:
    if it.x+vxy-plat.w<plat.x<it.x+vxy+it.w and it.y-plat.h<plat.y<it.y+it.h:return 1
   else:
    if it.x-plat.w<plat.x<it.x+it.w and it.y+vxy-plat.h<plat.y<it.y+vxy+it.h:return 1
class Enemy(Movable):
 def __init__(it,*arg):super().__init__(*list(arg)+[drawEnemy])
 def applyPhysics(it):
  if it.grounded:
   it2=it.grounded
   if it2.w>20:
    if it.x<it2.x+it.w:it.vx+=.05
    elif it.x>it2.x+it2.w-it.w*2:it.vx-=.05
    elif it.vx==0:it.vx=1
  super().applyPhysics();it.vx*=1.3
class Bullet(Movable):
 def __init__(it,it2):super().__init__(it2.x+it2.w/2,it2.y+it2.h/2,3,3,drawBullet);it.setVel(copysign(3,it2.vx)+it2.vx,it2.vy-1)
 def applyPhysics(it):it.addVel(0,.2);it.x+=it.vx;it.y+=it.vy
def fade(col1,col2,n):return tuple([i1*(1-n)+i2*n for i1,i2 in zip(I(col1),I(col2))])
def shadow(it):
 for i in range(1,6):F(int(it.x-i*2),int(it.y+it.h+(i-1)*5),it.w-i,5,fade("k",BG,i/6))
def baseEntity(it,col):
 F(int(it.x+2),int(it.y),it.w-2,it.h,col)
 if P:F(int(it.x),int(it.y),2,it.h,"k")
def drawPlayer(it):
 baseEntity(it,PL);F(int(it.x+it.w/2),int(it.y+7),2,3,"k")
 for i in(3,it.h-3):F(int(it.x+i+it.vx/4),int(it.y+2),2,2,"k")
def drawEnemy(it):
 baseEntity(it,NMY);F(int(it.x+2),int(it.y+it.h/2),it.w-2,it.h//2,NMY2)
 for i in(3,it.h-3):F(int(it.x+i),int(it.y+it.h/2-2),2,5,"k")
def hideEntity(it):F(int(it.x),int(it.y),int(it.w),int(it.h),BG)
def drawPlatform(it):
 baseEntity(it,PTF)
 if P:shadow(it)
def drawDoor(it):
 F(int(it.x),int(it.y),it.w,it.h,"k")
 for i in range(1,5):F(int(it.x+1),int(it.y+it.h-i*2),int(it.w/2-i*2+3),1,fade(PTF,"k",i/6))
def drawBullet(it):F(int(it.x),int(it.y),it.w,it.h,PTF)
def createDoor(l1):x,y,w,h=l1;x,y,w,h=x+w//2-7.5,y-15,15,15;return Entity(x,y,w,h,drawDoor)
def createPlayer(l1):x,y,w,h=l1;y,w,h=y-10,10,10;return Movable(x,y,w,h,drawPlayer)
def createEnemy(l1,id):x,y,w,h=l1;x,y,w,h=x+10*id,y-10,10,10;return Enemy(x,y,w,h)
def unpackPlatforms(n,type):
 global ghost;string=levels(type)[n];Hex2Int=lambda n:int("0x"+n);level,ghost=[],[int(string[0]),int(string[1])]
 for i in range(2,len(string),9):
  ptf_str=string[i:i+9];plat=[]
  for j in range(0,8,2):plat+=[Hex2Int(ptf_str[j:j+2])*5]
  plat.append(int(ptf_str[-1]));level.append(plat)
 return level
def loadLevel(lvl,type):
 file=unpackPlatforms(lvl,type);platforms,enemies=[],[]
 for i in file:
  platforms+=[Platform(*i[0:4])]
  if i[4]:enemies+=[createEnemy(i[0:4],j)for j in range(i[4])]
 player,door=createPlayer(file[0][0:4]),createDoor(file[-1][:4]);return [player,platforms,enemies,door]
def shortTransition():
 for i in range(30):F(0,0,320,222,fade(BG,"k",sin(i/30)));S(TARG_SPF/5)
class GameEngine():
 def __init__(it,lvl_type="original"):it.status,it.lvl_type,it.lvl_nb=0,lvl_type,len(levels(lvl_type));it.lvl,it.total_frame=0,0
 def nextLevel(it):
  global d
  if it.status==2:
   game.playTransition(mode=1)
   if it.lvl==21 and lvtype!="custom"and d==1:game.displayEndMsg(1);d+=1
   game.playTransition();it.lvl+=it.lvl_type=="hacked"
  else:shortTransition()
 def displayEndMsg(it,bis=0):
  t=game.total_frame*TARG_SPF;msg=["You finished the ","21 original levels !","","Virtual time: {} min {}s".format(t//60,round(t%60,2))]
  if it.lvl_type=="custom"or 1-bis:msg=[msg[3]]
  for i,txt in enumerate(msg):D(txt,160-len(txt)*5,111-(len(msg)//2-i)*20,PTF,BG)
  if bis:
   while K(4):0
   while 1-K(4):D("Press OK to play the",60,180,PTF,BG);D("8 new levels !",90,200,PTF,BG)
 def playTransition(it,h=222,mode=0):
  it.lvl+=1-mode and it.lvl_type!="hacked"and it.status==2
  try:l=loadLevel(it.lvl,it.lvl_type)
  except:return
  entities,s_init,y=l[1]+[l[3]],20,int(h)
  if not mode:
   entities+=[l[0]]+l[2]
   for entity in entities:entity.y-=y
  del l
  while 1:
   if y<1:break
   if mode:s=(1-y/h)*s_init+5
   else:s=sqrt(y/h)*s_init
   y=round(y-s,1);S(TARG_SPF*2);F(0,0,320,222,BG)
   for ent in entities:ent.y+=s;ent.draw()
 def playLevel(it):
  global platforms,player,P;F(0,0,320,222,BG);player,platforms,enemies,door=loadLevel(it.lvl,it.lvl_type);frame_nb,shot_frame,bullets,it.status=0,0,[],0
  while it.status==0:
   t=M();player.addVel(K(3)-K(0),.1*(1-K(4)))
   if frame_nb%10==0:door.draw()
   frame_nb+=1
   for b in bullets:
    hideEntity(b);b.applyPhysics()
    if b.y>222:bullets.remove(b)
    else:b.draw()
   for movable in enemies+[player]:
    hideEntity(movable)
    if movable!=player:
     if movable.hitBox(player):it.status=1;break
     for b in bullets:
      if b.hitBox(movable):movable.addVel(b.vx/3,b.vy/3)
     if movable.y>222:enemies.remove(movable)
     if game.lvl_type=="hacked"and movable.grounded and K(30):movable.jump()
    movable.applyPhysics()
    for plat in platforms:
     movable.onPlatform(plat)
     if(frame_nb+9)%10==0 and movable==player:plat.draw()
    movable.draw()
   if frame_nb-shot_frame>10 and K(16):shot_frame=frame_nb;bullets+=[Bullet(player)]
   if player.grounded:
    if K(4):player.jump()
    if player.hitBox(door):it.status=2
   if player.y>222:it.status=1
   while M()-t<TARG_SPF:0
   if K(17):
    D(" P A U S E D ",95,91,BG,PTF);D("Press [0] to restart level",30,111,BG,PTF);D("Press [shift] to",80,155,BG,PTF);D("enable/disable shadows",50,175,BG,PTF);frame_nb+=10-frame_nb%10
    while K(17):0
    while(K(17)|K(48)|K(12))^1:0
    while K(17)|K(48)|K(12):
     if K(48):it.status=1
     elif K(12):P=1-P
    F(95,91,130,20,BG);F(30,111,260,20,BG);F(80,155,160,20,BG);F(50,175,220,20,BG)
  it.total_frame+=frame_nb
def levels(lvl_type):
 if lvl_type!="custom":return("00011606010071632010391606010",
"00011606010111606010231606010341606010",
"000216060101e1601010331606010",
"10102106010160f03130051d0b01005160b01005100b01019211d010",
"00042306010161c060100e1410011290f06011350f06010",
"0008280601026280a011342102010291b020100f1b0201003140a011190d0201026080a010",
"000624060101a28020102b220201031190a01118160a0110a0e020101b070201030070a010",
"000527060101927010102a27010103420060112819060110c19060110511010100b09010101a060a013330606010",
"000428060100c2001010041c010100c16010102f1e01010391801010301101010260b0601012070a010",
"000128060101526010103026010103921010102d1a010101a1501010230e01010310d0a010",
"00052506010051e06011051606011050f06011050806011301806010",
"000628060101623060110c1e020101719020102b19020103b12020102b0c02010170c02010060906010",
"000228060101d28060103122060111d1d020100e1b020100515020100e0f02010240d06011320d06010",
"000105040100c05350100823010101a1f060111e1801010341206011280e06010280e06010",
"00362706010292006011351a020102f1302010042006011041706011080f010100c0706010",
"00032806010242806011342106011251906011081901010021201010090c01010150706011270706010",
"00042806010192306011251e06010311a060112514060100316060110a0e01010140806010",
"003726060101f2601010042601010081e010101019010102519010102f1006011220b06010",
"001a28060102a2306010331b01010251701010141706011051201010100a010101c0606010",
"000327060100c20010101619010102119030112d1903011351306010260a060111a0a01010050a06010",
"000220060101d1b020113117020111c0f06010",
"002f28060101428040110422020100e1b04010271a04011321402010270e040110e0e04011040806010",
"110728020101e2301010081e010111e1a010111e1101011081501011080c010111e08010110803010111c00050123800000",
"11002b020101725010102a28010103a20010103618010102e11010103a0901010360201010260201010000e0c0120916010a0031601061011601071051601051042001010",
"113c29010103c1f000112e26000102e0e00011202400010172800010171201110062401010121c01010021601010370e010703a1804010",
"1108270101000270a0130a27050121a00000111a1f01011281705010250f0b011240700010060c00013020000013",
"112423060102d2302011362102011361a02011361402011360e020113608020112407020110f070201102140e011081d04010",
"112c1f060102a13020d02f28020102a28010111e28030111423010111f1d01011141801011201301011280e01011330e010113b0a010113804010112c04010111c0401011100701011030401011",
"11002702010011f000111626010112626010103020010103819010102f1401010380c01010380401010250a010102903010c00e1601011050206010050601060000701010000f010100a0301070090901010",
"113b2b01010282718012372b01010322b010102d2b01010292b01010252b010102e21010612e1a010112e14010112e0e010112e09010112e04010110b1802010031201011030c01011020603010")
 else:return("1101023f2b0",'')#your lvl here

#--------------------
lvtype="custo"
#"custom" or "hacked"
#--------------------
platforms,player,ghost=[],0,[0,0];game=GameEngine(lvtype);game.playTransition();d=1
try:
 while game.lvl<game.lvl_nb:game.playLevel();game.nextLevel()
except KeyboardInterrupt:d=0
if d:game.displayEndMsg()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.