Sample game is available at http://www.mediafire.com/download/9fli0 ... .0-all.zip
This framework contains 3 scripts: script.rpy, dungeon.rpy and battle.rpy. You can omit dungeon.rpy or battle.rpy if you don't need it.
This framework only works in ren'py 6.17 or later. Rewriting styles allows to work in renpy 6.16 or before, but not recommended because of performance reason.
Basic python knowledge is highly recommended.
This framework is in the public domain.
script.rpy
Code: Select all
# This file is in the public domain.
label start:
# Initializing data
python:
# Create skills (name, type, hit, power)
attack = Skill("Attack", "attack", 70, 20)
escape = Skill("Escape", "escape")
# Create battle actors (name, max_hp, skills)
hero = Actor("Hero",100, [attack,escape])
goblin = Actor("Goblin",40, [attack])
# Create a dungeon stage (map,enemy)
# "1" means wall, "0" means path.
stage1=Stage([
"1111111111",
"1111011001",
"1000000001",
"1110111101",
"1000000001",
"1111111111",
],
enemy=goblin)
# The game starts here.
# Place a player position on a dungeon stage (stage,y,x,dy,dx).
# dx,dy means direction. If dy=1, it's down. If dx=-1, it's left.
$ here=Coordinate(stage1,2,2,0,1)
# To start exploring, call or jump to the label dungeon.
call dungeon
# To start battling, call the label battle with 2 actor objects: player and enemy.
call battle(hero,goblin)
Code: Select all
# This file is in the public domain.
init -1 python:
class Stage(object):
'''
Class which contains map itself, auto mapping record, and encounter enemy.
'''
def __init__(self, map, enemy=None):
self.map=map
self.enemy=enemy
self.mapped=[]
for n,i in enumerate(map):
self.mapped.append([])
for j in i:
self.mapped[n].append(0)
class Coordinate(object):
'''
Class used for calculating relative coordinate.
'''
def __init__(self, stage=None, y=0, x=0, dy=0, dx=0):
self.stage=stage
self.y=y
self.x=x
self.dy=dy
self.dx=dx
class Minimap(object):
'''
A minimap. Minimap(current_coordinate).sm is a displayable to show this minimap.
'''
def __init__(self,child):
self.sm = SpriteManager(ignore_time=True)
for i in xrange(len(child.stage.map)):
for j in xrange(len(child.stage.map[0])):
if child.stage.mapped[i][j]==1:
if child.stage.map[i][j] in ["1"]:
d = Solid("#666", xysize=(12,12))
else:
d = Solid("#fff9", xysize=(12,12))
else:
d = Solid("#0000", xysize=(12,12))
self.add(d,i,j)
if child.dy==-1:
self.add(Text("↑",size=12),child.y,child.x)
elif child.dx==1:
self.add(Text("→",size=12),child.y,child.x)
elif child.dy==1:
self.add(Text("↓",size=12),child.y,child.x)
else:
self.add(Text("←",size=12),child.y,child.x)
def add(self, d,n,m):
s = self.sm.create(d)
s.x = m*12+12
s.y = n*12+12
screen move:
# Screen which shows move buttons and a minimap
fixed style_group "move":
if front1.stage.map[front1.y][front1.x] is not "1":
textbutton "↑" action Return(value=front1) xcenter .2 ycenter .7
textbutton "→" action Return(value=turnright) xcenter .3 ycenter .8
textbutton "↓" action Return(value=turnback) xcenter .2 ycenter .9
textbutton "←" action Return(value=turnleft) xcenter .1 ycenter .8
add Minimap(here).sm
style move_button_text:
size 60
# Assign background images.
# "left0" means a wall on the lefthand, "front2" means a further wall on the front, and so on.
# left2, front2, right2
# left1, front1, right1
# left0, here , right0
image floor = "floor.png"
image left0 = "left0.png"
image right0 = Transform("left0.png", xzoom=-1)
image front1 ="front1.png"
image left1 = "left1.png"
image right1 = Transform("left1.png", xzoom=-1)
image front2 = "front2.png"
image left2 = "left2.png"
image right2 = Transform("left2.png", xzoom=-1)
label dungeon:
# To start exploring, call or jump to this label
# To exit, create an event which has return or jump statement.
while True:
# Calculate relative coordinates
python:
turnright=Coordinate(here.stage, here.y,here.x, here.dx,-here.dy)
turnleft=Coordinate(here.stage, here.y, here.x, -here.dx,here.dy)
turnback=Coordinate(here.stage, here.y,here.x, -here.dy,-here.dx)
right0=Coordinate(here.stage, here.y+here.dx,here.x-here.dy, here.dy,here.dx)
left0=Coordinate(here.stage, here.y-here.dx,here.x+here.dy, here.dy,here.dx)
front1=Coordinate(here.stage, here.y+here.dy,here.x+here.dx, here.dy,here.dx)
right1=Coordinate(here.stage, front1.y+front1.dx,front1.x-front1.dy, here.dy,here.dx)
left1=Coordinate(here.stage, front1.y-front1.dx,front1.x+front1.dy, here.dy,here.dx)
front2=Coordinate(here.stage, front1.y+front1.dy,front1.x+front1.dx, here.dy,here.dx)
right2=Coordinate(here.stage, front2.y+front2.dx,front2.x-front2.dy, here.dy,here.dx)
left2=Coordinate(here.stage, front2.y-front2.dx,front2.x+front2.dy, here.dy,here.dx)
# Composite background images. Try-except clauses are used to prevent the List Out of Index Error
scene
show floor
python:
for i in ["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]:
try:
j=globals()[i]
if j.stage.map[j.y][j.x]=="1":
renpy.show(i)
except:
pass
# Record maps
python:
for i in [left1, right1, front1, left0, right0, here]:
here.stage.mapped[i.y][i.x]=1
# Check events. If it happens, call a label or jump out to a label.
if here.stage.enemy is not None and renpy.random.random()< .2:
call battle(player=hero, enemy=here.stage.enemy)
# Otherwise, call the move screen
$ renpy.block_rollback()
call screen move
$ here=_return
Code: Select all
# This file is in the public domain.
init -1 python:
from copy import copy
class Skill(object):
'''
Class used for battle skills
'''
def __init__(self, name, type, hit=0, power=0):
self.name = name
self.type = type
self.hit = hit
self.power = power
class Actor(object):
'''
Class used for battle characters.
'''
def __init__(self, name, max_hp=0, skills=[]):
self.name=name
self.max_hp=max_hp
self.hp=max_hp
self.skills = skills
def attack(self,skill,target):
if self.skill.hit < renpy.random.randint (0,100):
narrator ("{} dodged {}'s attack".format(target.name,self.name))
else:
target.hp -= self.skill.power
narrator ("{} got {} damage".format(target.name, self.skill.power))
screen battle_ui:
# Screen which shows battle status
use battle_frame(char=player, position=(.95,.05))
use battle_frame(char=enemy, position=(.05,.05))
screen battle_frame(char,position):
# Screen included in the battle_ui screen
frame xysize(180, 80) align position:
vbox yfill True:
text "[char.name]"
hbox xfill True:
text "HP"
text "[char.hp]/[char.max_hp]" xalign 1.0
screen command_screen:
# Screen used for selecting skills
vbox style_group "skill" align (.1,.8):
for i in player.skills:
textbutton "[i.name]" action Return (value=i)
style skill_button_text:
size 40
xminimum 200
label battle(player, enemy):
# To start battling, call this label with 2 actor objects: player and enemy.
# Preparation
# Copying enemy object prevents modifying an original data.
$ enemy=copy(enemy)
$ _rollback=False
show screen battle_ui
"[enemy.name] appeared"
# Main phase
call _battle(player, enemy)
# Result
if _return is "lose":
"Gameover"
$renpy.full_restart()
elif _return is "win":
"You won"
elif _return is "escape":
"You escaped"
hide screen battle_ui
$ _rollback=True
return
label _battle(player, enemy):
# A sub label used in the battle label.
while True:
$ player.skill = renpy.call_screen("command_screen")
$ enemy.skill = renpy.random.choice(enemy.skills)
if player.skill.type=="escape":
return "escape"
$ player.attack(player.skill, enemy)
if enemy.hp < 1:
return "win"
$ enemy.attack(enemy.skill, player)
if player.hp < 1:
return "lose"