Compare commits

...

6 Commits

Author SHA1 Message Date
theo@manjaro 706b7e0d92 Updated game over screen 2021-09-19 16:58:39 +02:00
theo@manjaro aa631c188e Added reset button 2021-09-19 15:58:01 +02:00
theo@manjaro f968827b7b Reduce camera shaking on spawn 2021-09-19 15:50:36 +02:00
theo@manjaro 74e05b11d2 Added monster behavior 2021-09-19 15:49:34 +02:00
theo@manjaro 693e4a49e8 Monsters kill other pinmiks 2021-09-19 15:07:59 +02:00
theo@manjaro eca2323d1a Added pause menu 2021-09-19 12:20:43 +02:00
45 changed files with 122 additions and 17 deletions

View File

@ -1,12 +1,12 @@
# Textures # Textures
All the textures are edited by myself to fit the [Famicube palette](https://lospec.com/palette-list/famicube) I edited some textures to fit the [Famicube palette](https://lospec.com/palette-list/famicube)
## Tileset ## Tileset
[Buch - OpenGameArt](https://opengameart.org/content/the-field-of-the-floating-islands) [Buch - OpenGameArt](https://opengameart.org/content/the-field-of-the-floating-islands)
## Little characters ## "Pinmik" sprite ( I made the recolors )
[GraphxKid - OpenGameArt](https://opengameart.org/content/arcade-platformer-assets) [GraphxKid - OpenGameArt](https://opengameart.org/content/arcade-platformer-assets)
@ -21,3 +21,7 @@ All the textures are edited by myself to fit the [Famicube palette](https://losp
## Font ## Font
[Front End Dev - Grape Soda](https://fontenddev.com/fonts/grape-soda/) [Front End Dev - Grape Soda](https://fontenddev.com/fonts/grape-soda/)
## Dust particles
[GraphxKid - OpenGameArt](https://opengameart.org/content/items-and-elements)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

View File

@ -136,6 +136,10 @@ def getunlocks(highscore):
unlocks["normal"].append("green") unlocks["normal"].append("green")
if highscore>=250: if highscore>=250:
unlocks["normal"].append("blue") unlocks["normal"].append("blue")
if highscore>=500: if highscore>=400:
unlocks["specials"].append("gold") unlocks["specials"].append("gold")
if highscore>=500:
unlocks["specials"].append("monster")
if highscore>=750:
unlocks["specials"].append("platinum")
return unlocks return unlocks

View File

@ -58,6 +58,7 @@ class Game():
self.globals["scameray"] = 0 self.globals["scameray"] = 0
self.globals["debug"] = False self.globals["debug"] = False
self.globals["highscore"] = highscore self.globals["highscore"] = highscore
self.globals["pause"] = False
self.scaleCamera() self.scaleCamera()
self.globals["bgmvolume"] = bgm self.globals["bgmvolume"] = bgm

View File

@ -7,6 +7,7 @@ class BaseObject():
self.game = game self.game = game
self.sprite = game.sprite_lib["icon.png"] self.sprite = game.sprite_lib["icon.png"]
self.spriteoffset = 0,0 self.spriteoffset = 0,0
self.handlepause = False
self.depth = 1 # Sa "profondeur", déterminera l'odre d'affichage des objets self.depth = 1 # Sa "profondeur", déterminera l'odre d'affichage des objets
def step(self): def step(self):

View File

@ -40,6 +40,7 @@ class GameLoop():
objs = list(self.objects.values()) objs = list(self.objects.values())
for i in objs: for i in objs:
if i.handlepause and not game.globals["pause"] or not i.handlepause:
i.step() i.step()
def draw(self,game): def draw(self,game):

View File

@ -1,6 +1,8 @@
from gamedata.objects.base import BaseObject from gamedata.objects.base import BaseObject
from gamedata.objects.button import Button from gamedata.objects.button import Button
import random
class GameOver(BaseObject): class GameOver(BaseObject):
def __init__(self,game): def __init__(self,game):
super().__init__(0,0,game) super().__init__(0,0,game)
@ -14,6 +16,8 @@ class GameOver(BaseObject):
btn.click = fnBack btn.click = fnBack
game.gameloop.summon(btn) game.gameloop.summon(btn)
self.depth = -1
# Updating highscore # Updating highscore
self.highscore = False self.highscore = False
self.newunlocks = [] self.newunlocks = []
@ -70,6 +74,28 @@ class GameOver(BaseObject):
# Launch particles # Launch particles
self.launched = True self.launched = True
if self.highscore: # Balloons
offy = self.game.DISPLAY_HEIGHT+40
for i in range(2):
for direction in [-1,1]:
for color in ["yellow","red","blue"]:
sprite = [self.game.sprite_lib["particles/balloons/"+color+".png"]]
offx = self.game.DISPLAY_WIDTH/2-direction*self.game.DISPLAY_WIDTH/2*1.2
velx = (random.random()*3+1+2*i)*direction/2
vely = (random.random()+2)*-1
self.game.addParticle(sprite,offx,offy,velx=velx,vely=vely,flipx=direction==-1,fps=0.3)
# Confettis
for j in range(30):
for i in range(8):
color = random.choice(["red","blue","yellow"])
sprites = self.game.getSpriteDir("particles/confetti/"+color+"/")
offx = random.randint(0,self.game.DISPLAY_WIDTH)
offy = -40*i
velx = random.random()*2-1
vely = random.random()*5+2
self.game.addParticle(sprites,offx,offy,velx=velx,vely=vely,flipx=random.randint(0,1),fps=2)
if self.launched: if self.launched:
self.flashtimer.tick(self.game.dt) self.flashtimer.tick(self.game.dt)
@ -78,6 +104,9 @@ class GameOver(BaseObject):
self.displayscore = int(self.game.globals["score"]) self.displayscore = int(self.game.globals["score"])
def draw(self): def draw(self):
# Display night sky
self.game.window.blit(self.game.pygame.transform.scale(self.game.sprite_lib["skies/night.png"],[self.game.DISPLAY_WIDTH,self.game.DISPLAY_HEIGHT]),[0,0])
self.game.window.blit(self.game.sprite_lib["vignette.png"],[0,0])
# Display score # Display score
txt = self.game.fontfilebig.render("Score : "+str(int(self.displayscore)),False,self.color) txt = self.game.fontfilebig.render("Score : "+str(int(self.displayscore)),False,self.color)
txt = self.game.pygame.transform.scale(txt,(round(txt.get_width()*self.scale),round(txt.get_height()*self.scale))) txt = self.game.pygame.transform.scale(txt,(round(txt.get_width()*self.scale),round(txt.get_height()*self.scale)))

View File

@ -11,6 +11,8 @@ class Clouds(BaseObject):
self.depth = -2 self.depth = -2
self.horoffset = 0 self.horoffset = 0
self.handlepause = True
def step(self): def step(self):
self.horoffset+=self.game.dt*self.speed self.horoffset+=self.game.dt*self.speed

View File

@ -13,13 +13,24 @@ class Lemming(BaseObject):
self.normalspeed = self.basespeed # Speed "objective" self.normalspeed = self.basespeed # Speed "objective"
self.speed = 0 # Current speed, leaning towards objective speed self.speed = 0 # Current speed, leaning towards objective speed
self.scoreratio = 0.2 self.handlepause = True
self.scoreratio = 0.3
if skin=="gold": if skin=="gold":
self.basespeed*=1.2 self.basespeed*=1.2
self.scoreratio*=3 self.scoreratio*=3
if skin=="platinum":
self.basespeed*=1.4
self.scoreratio*=5
if skin=="monster":
self.scoreratio*=6
self.skin = skin self.skin = skin
self.maxhealth = 2 # Pinmiks can be attacked by the monsters
self.health = self.maxhealth
self.regen = 1
self.selected = False # If beeing redirected self.selected = False # If beeing redirected
self.anglemargin = 25 self.anglemargin = 25
@ -59,6 +70,22 @@ class Lemming(BaseObject):
self.move(diffx*self.game.dt,diffy*self.game.dt) self.move(diffx*self.game.dt,diffy*self.game.dt)
self.health = min(self.maxhealth,self.health+self.regen*self.game.dt) # Regen live
# Attacking other pinmiks
if self.skin=="monster":
for pinmik in self.game.gameloop.findname("Lemming"):
if pinmik.skin!="monster":
if self.rect.collidepoint(pinmik.rect.center):
pinmik.health-=self.game.dt*3.5
if self.holdrect.collidepoint(pinmik.rect.center):
pinmik.health-=self.game.dt*2
# Lean towards middle
angle_to_mid = math.degrees(math.atan2(self.game.DISPLAY_HEIGHT/2-self.rect.center[1],self.game.DISPLAY_WIDTH/2-self.rect.center[0]))
if abs(angle_to_mid-self.direction-360)<abs(angle_to_mid-self.direction):
angle_to_mid-=360
self.direction += (angle_to_mid-self.direction)*self.game.dt*0.5
# Spawning dust particles if being launched # Spawning dust particles if being launched
if self.speed > self.basespeed*1.5: if self.speed > self.basespeed*1.5:
if self.dusttimer.tick(self.game.dt*(self.speed/self.basespeed)**2): if self.dusttimer.tick(self.game.dt*(self.speed/self.basespeed)**2):
@ -92,7 +119,8 @@ class Lemming(BaseObject):
if 0<=gridx<len(self.tiles.grid[gridy]): if 0<=gridx<len(self.tiles.grid[gridy]):
if self.tiles.grid[gridy][gridx]==1: if self.tiles.grid[gridy][gridx]==1:
dead = False dead = False
if dead: if dead or self.health<0:
if self.skin!="monster": # Monsters die without removing lives
self.manager.death() self.manager.death()
if self.game.globals["scamerax"]*self.game.globals["scameray"]>5: # Avoiding shaking to much if self.game.globals["scamerax"]*self.game.globals["scameray"]>5: # Avoiding shaking to much
self.game.globals["scamerax"]+=1 self.game.globals["scamerax"]+=1
@ -135,12 +163,21 @@ class Lemming(BaseObject):
self.holdrect[1]+=int(vely) self.holdrect[1]+=int(vely)
def draw(self): def draw(self):
if self.skin=="monster":
# Red aura
self.game.lib.drawcenter(self.game,self.game.sprite_lib["lemmings/monster/aura.png"],self.rect.center[0],self.rect.center[1])
# Shaking based on life
offx,offy = 0,0
if self.health<self.maxhealth:
shake = (1-(self.health/self.maxhealth))*4
offx = random.random()*shake*2-shake
offy = random.random()*shake*2-shake
orientation = self.orientations[int(self.direction%360/361*4)] orientation = self.orientations[int(self.direction%360/361*4)]
sprites = self.sprites[orientation] sprites = self.sprites[orientation]
self.game.lib.drawcenter(self.game,self.game.sprite_lib["lemmings/shadow.png"],self.rect.center[0]-self.game.globals["camerax"],self.rect.center[1]-self.game.globals["cameray"]) self.game.lib.drawcenter(self.game,self.game.sprite_lib["lemmings/shadow.png"],self.rect.center[0]-self.game.globals["camerax"]+offx,self.rect.center[1]-self.game.globals["cameray"]+offy)
self.game.lib.drawcenter(self.game,sprites[int(self.spriteindex)%len(sprites)],self.rect.center[0]-self.game.globals["camerax"],self.rect.center[1]-self.game.globals["cameray"]) self.game.lib.drawcenter(self.game,sprites[int(self.spriteindex)%len(sprites)],self.rect.center[0]-self.game.globals["camerax"]+offx,self.rect.center[1]-self.game.globals["cameray"]+offy)
if self.selected: if self.selected:
self.game.lib.drawcenter(self.game,self.game.sprite_lib["lemmings/selected.png"],self.rect.center[0]-self.game.globals["camerax"],self.rect.center[1]-self.game.globals["cameray"]) self.game.lib.drawcenter(self.game,self.game.sprite_lib["lemmings/selected.png"],self.rect.center[0]-self.game.globals["camerax"]+offx,self.rect.center[1]-self.game.globals["cameray"]+offy)
if self.game.globals["debug"]: if self.game.globals["debug"]:
s = self.game.pygame.Surface(self.rect.size) s = self.game.pygame.Surface(self.rect.size)
s.fill([255,0,0]) s.fill([255,0,0])

View File

@ -52,8 +52,12 @@ class Manager(BaseObject):
lemmings = self.game.gameloop.findname("Lemming") lemmings = self.game.gameloop.findname("Lemming")
nblemmings = len(lemmings) + len(self.game.gameloop.findname("Spawner")) nblemmings = len(lemmings) + len(self.game.gameloop.findname("Spawner"))
# Updating score # Updating score
if not self.game.globals["pause"]:
for lemming in lemmings: for lemming in lemmings:
self.score+=lemming.scoreratio*self.game.dt self.score+=lemming.scoreratio*self.game.dt
# Pausing the game
if self.game.inputs["keys"]["escape"]["timer"]==1:
self.game.globals["pause"] = not self.game.globals["pause"]
# Spawning more lemmings # Spawning more lemmings
if (self.lives>0 and nblemmings>0): if (self.lives>0 and nblemmings>0):
if self.spawntimer.tick(self.game.dt): if self.spawntimer.tick(self.game.dt):
@ -65,6 +69,7 @@ class Manager(BaseObject):
if self.endtimer.tick(self.game.dt): if self.endtimer.tick(self.game.dt):
self.game.globals["score"] = int(self.score) self.game.globals["score"] = int(self.score)
self.game.scene = self.game.scenes.gameover self.game.scene = self.game.scenes.gameover
self.game.globals["pause"] = False
if self.invincible: if self.invincible:
if self.deathtimer.tick(self.game.dt): if self.deathtimer.tick(self.game.dt):
@ -105,9 +110,8 @@ class Manager(BaseObject):
self.game.realwindow.blit(txtsurfacelives,[20,20*2+txtsurfacescore.get_height()]) self.game.realwindow.blit(txtsurfacelives,[20,20*2+txtsurfacescore.get_height()])
self.endrect.set_alpha((1-self.endtimer.getratio())*255) self.endrect.set_alpha((1-self.endtimer.getratio())*255)
if self.game.globals["pause"]:
self.endrect.set_alpha(100)
if self.endtimer.getloops()>=1: if self.endtimer.getloops()>=1:
self.endrect.set_alpha(255) self.endrect.set_alpha(255)
self.game.realwindow.blit(self.endrect,[0,0]) self.game.realwindow.blit(self.endrect,[0,0])

View File

@ -21,6 +21,8 @@ class Skies(BaseObject):
self.blackrectalpha = 0.5 self.blackrectalpha = 0.5
self.handlepause = True
def step(self): def step(self):
self.timeelapsed+=self.game.dt self.timeelapsed+=self.game.dt

View File

@ -12,6 +12,8 @@ class Spawner(BaseObject):
self.distance = game.DISPLAY_HEIGHT self.distance = game.DISPLAY_HEIGHT
self.timer = game.lib.Timer(2.5+random.random()) # Seconds of telegraph before spawning the lemming self.timer = game.lib.Timer(2.5+random.random()) # Seconds of telegraph before spawning the lemming
self.handlepause = True
# Skin choosing # Skin choosing
self.skin = random.choice(skins["normal"]) self.skin = random.choice(skins["normal"])
if random.randint(1,10)==1: # 1/10 chance to have a special if random.randint(1,10)==1: # 1/10 chance to have a special
@ -28,8 +30,8 @@ class Spawner(BaseObject):
lemming.move(-lemming.rect[2]/2,-lemming.rect[3]/2) lemming.move(-lemming.rect[2]/2,-lemming.rect[3]/2)
self.game.gameloop.summon(lemming) self.game.gameloop.summon(lemming)
self.game.gameloop.delid(self.id) self.game.gameloop.delid(self.id)
self.game.globals["scamerax"]+=3 self.game.globals["scamerax"]+=2
self.game.globals["scameray"]+=3 self.game.globals["scameray"]+=2
# Spawn dust particles # Spawn dust particles
sprites = self.game.getSpriteDir("particles/dust/") sprites = self.game.getSpriteDir("particles/dust/")

View File

@ -0,0 +1,15 @@
from gamedata.objects.button import Button
class Reset(Button):
def __init__(self,x,y,game,w,h):
super().__init__(x,y,game,w,h)
self.text = "Reset save"
def reset(self,game):
game.globals["highscore"] = 0
game.scene = game.scenes.main
self.click = reset

View File

@ -9,6 +9,7 @@ from gamedata.objects.ingame.manager import Manager
from gamedata.objects.gameover import GameOver from gamedata.objects.gameover import GameOver
from gamedata.objects.ingame.skies import Skies from gamedata.objects.ingame.skies import Skies
from gamedata.objects.ingame.clouds import Clouds from gamedata.objects.ingame.clouds import Clouds
from gamedata.objects.resetbutton import Reset
def main(game): def main(game):
game.scaleCamera() game.scaleCamera()
@ -38,9 +39,11 @@ def options(game):
game.globals["cameray"] = 0 game.globals["cameray"] = 0
s = BGMSlider(40,40,game,400,40) s = BGMSlider(40,40,game,400,40)
s2 = SFXSlider(40,100,game,400,40) s2 = SFXSlider(40,100,game,400,40)
btn = Reset(40,160 ,game,400,40)
menu = OptionMenu(round(game.DISPLAY_WIDTH/8),round(game.DISPLAY_HEIGHT*9/10),game,round(game.DISPLAY_WIDTH*6/8),round(game.DISPLAY_HEIGHT/10-game.DISPLAY_HEIGHT/30)) menu = OptionMenu(round(game.DISPLAY_WIDTH/8),round(game.DISPLAY_HEIGHT*9/10),game,round(game.DISPLAY_WIDTH*6/8),round(game.DISPLAY_HEIGHT/10-game.DISPLAY_HEIGHT/30))
game.gameloop.summon(s) game.gameloop.summon(s)
game.gameloop.summon(s2) game.gameloop.summon(s2)
game.gameloop.summon(btn)
game.gameloop.summon(menu) game.gameloop.summon(menu)
def gameover(game): def gameover(game):