commit 54093995d1cf6ba9cb2dca2027b5542154a2415a
parent 5760aecfb53d23e86bd73b85db4dcb2ae934d21b
Author: Erik Letson <hmagellan@hmagellan.com>
Date: Sun, 22 Nov 2020 15:39:07 -0600
big improvement to piece loading, levelup, active stat calc
Diffstat:
6 files changed, 196 insertions(+), 235 deletions(-)
diff --git a/data/board/testmap1/testmap1.json b/data/board/testmap1/testmap1.json
@@ -1,251 +1,94 @@
{
- "testent1" : {
- "name" : "Jisella",
- "type" : "Piece",
- "sheet" : "jisella_1",
- "sprite" : [0, 0],
- "pictures" : "jisella_pictures_1",
+ "testent1" : {
+ "name" : "Jisella",
+ "template" : "Jisella",
+ "level" : 1,
+ "rank" : 0,
+ "exp" : 0,
"visible" : true,
"animation" : "stand_L",
"animated" : true,
"passable" : false,
"tile" : [5, 4],
"team" : "Player",
- "normal_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 9,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 2,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Fire",
- "EXPERTISE" : ["Sword"]
- },
- "active_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 80,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 9,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 2,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Fire",
- "EXPERTISE" : ["Sword"]
- },
"equipment" : {
"weapon" : "Basic_Sword_1",
"armor" : "Basic_Armor_1",
"acc1" : null,
"acc2" : null,
"acc3" : null
- }
- },
+ },
+ "stat_mod" : { },
+ "stat_dist" : { },
+ "replace" : { }
+ },
"testent2" : {
"name" : "Fakella",
- "type" : "Piece",
- "sheet" : "jisella_2",
- "sprite" : [0, 0],
- "pictures" : "jisella_pictures_2",
+ "template" : "Jisella",
+ "level" : 2,
+ "rank" : 0,
+ "exp" : 100,
"visible" : true,
- "animation" : "stand_R",
+ "animation" : "stand_L",
"animated" : true,
"passable" : false,
- "tile" : [0, 2],
- "team" : "Enemy",
- "normal_stats" : {
- "LVL" : 1,
- "EXP" : 100,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 3,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 2,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
- "active_stats" : {
- "LVL" : 1,
- "EXP" : 100,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 3,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 2,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
+ "tile" : [0, 3],
+ "team" : "Player",
"equipment" : {
"weapon" : "Basic_Sword_1",
"armor" : "Basic_Armor_1",
"acc1" : null,
"acc2" : null,
"acc3" : null
- }
+ },
+ "stat_mod" : { },
+ "stat_dist" : { },
+ "replace" : { "sheet" : "jisella_2", "pictures" : "jisella_pictures_2" }
},
"testent3" : {
- "name" : "OtherName",
- "type" : "Piece",
- "sheet" : "jisella_4",
- "sprite" : [0, 0],
- "pictures" : "jisella_pictures_4",
+ "name" : "Faker2",
+ "template" : "Jisella",
+ "level" : 1,
+ "rank" : 0,
+ "exp" : 0,
"visible" : true,
- "animation" : "stand_R",
+ "animation" : "stand_L",
"animated" : true,
"passable" : false,
- "tile" : [7, 2],
+ "tile" : [6, 1],
"team" : "Enemy",
- "normal_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 1,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 6,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
- "active_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 1,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 6,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
"equipment" : {
"weapon" : "Basic_Sword_1",
"armor" : "Basic_Armor_1",
"acc1" : null,
"acc2" : null,
"acc3" : null
- }
+ },
+ "stat_mod" : { },
+ "stat_dist" : { },
+ "replace" : { "sheet" : "jisella_3", "pictures" : "jisella_pictures_3" }
},
"testent4" : {
"name" : "SomeName",
- "type" : "Piece",
- "sheet" : "jisella_3",
- "sprite" : [0, 0],
- "pictures" : "jisella_pictures_3",
+ "template" : "Jisella",
+ "level" : 8,
+ "rank" : 0,
+ "exp" : 100,
"visible" : true,
- "animation" : "stand_R",
+ "animation" : "stand_L",
"animated" : true,
"passable" : false,
- "tile" : [0, 9],
+ "tile" : [2, 2],
"team" : "Enemy",
- "normal_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 100,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 11,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 9,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
- "active_stats" : {
- "LVL" : 1,
- "EXP" : 0,
- "HP" : 10,
- "ATK" : 10,
- "DEF" : 6,
- "SPD" : 11,
- "ACC" : 7,
- "INT" : 5,
- "WIS" : 6,
- "LUK" : 8,
- "INIT" : 9,
- "CNTR" : 2,
- "GARD" : 1,
- "PUSH" : 2,
- "MOVE" : 6,
- "SKIL" : 3,
- "PASS" : 3,
- "AFFINITY" : "Water",
- "EXPERTISE" : ["Sword"]
- },
"equipment" : {
"weapon" : "Basic_Sword_1",
"armor" : "Basic_Armor_1",
"acc1" : null,
"acc2" : null,
"acc3" : null
- }
+ },
+ "stat_mod" : { },
+ "stat_dist" : { },
+ "replace" : { "sheet" : "jisella_4", "pictures" : "jisella_pictures_4" }
}
}
-
diff --git a/data/img/stat_screen_1.png b/data/img/stat_screen_1.png
Binary files differ.
diff --git a/data/img/weapon_icons_1.png b/data/img/weapon_icons_1.png
Binary files differ.
diff --git a/data/json/pieces.json b/data/json/pieces.json
@@ -0,0 +1,40 @@
+{
+ "Jisella" : {
+ "type" : "Piece",
+ "sheet" : "jisella_1",
+ "pictures" : "jisella_pictures_1",
+ "class" : "Adventurer",
+ "affinity" : "Fire",
+ "expertise" : ["Sword"],
+ "stats" : {
+ "HP" : 20,
+ "ATK" : 6,
+ "DEF" : 3,
+ "SPD" : 5,
+ "ACC" : 4,
+ "INT" : 2,
+ "WIS" : 2,
+ "LUK" : 4,
+ "INIT" : 3,
+ "CNTR" : 2,
+ "GARD" : 1,
+ "PUSH" : 2,
+ "MOVE" : 6,
+ "SKIL" : 3,
+ "PASS" : 4
+ },
+ "growth" : {
+ "2" : { "ATK" : 1 },
+ "3" : { "SPD" : 1, "DEF" : 1 },
+ "4" : { "ACC" : 1, "WIS" : 1, "INT" : 1 },
+ "5" : { "LUK" : 1, "SPD" : 1 },
+ "7" : { "ATK" : 1, "ACC" : 1 },
+ "10": { "DEF" : 1, "ATK" : 1 },
+ "11": { "ATK" : 1, "WIS" : 1, "INT" : 1 },
+ "13": { "LUK" : 2, "ACC" : 1 },
+ "17": { "SPD" : 1, "INT" : 1 },
+ "19": { "ATK" : 2, "SPD" : 2 },
+ "20": { "SPD" : 1, "WIS" : 1, "ACC" : 1 }
+ }
+ }
+}
diff --git a/src/constants.py b/src/constants.py
@@ -40,6 +40,10 @@ JSON_PATH = os.path.join(DATA_PATH, "json")
MENU_JSON_PATH = os.path.join(JSON_PATH, "menus")
SCENE_JSON_PATH = os.path.join(JSON_PATH, "scenes")
+# Piece constants
+CHARBASE = json.load(open(os.path.join(JSON_PATH, "pieces.json")))
+AFFINITIES = ("Fire", "Miasma", "Water", "Wind", "Life", "Lightning")
+
# Item constants
ITEMBASE = json.load(open(os.path.join(JSON_PATH, "items.json")))
WEAPON_TYPE_SOURCES = {
diff --git a/src/piece.py b/src/piece.py
@@ -1,4 +1,4 @@
-import pygame, os, json, random
+import pygame, os, json, random, copy
from . import manager, entity
from .constants import *
@@ -52,37 +52,77 @@ class PieceManager(manager.Manager):
# here. This should probably be so for all methods that load
# from definition.
for p in definition:
- n_sheet = self.bus.fetch("sheet_manager", "sheets")[definition[p]["sheet"]]
- n_sprite = tuple(definition[p]["sprite"])
- n_anim = self.bus.fetch("sheet_manager", "animations")[definition[p]["sheet"]][definition[p]["animation"]]
- n_animated = definition[p]["animated"]
- n_name = definition[p]["name"]
- n_pictures = definition[p]["pictures"]
- n_passable = definition[p]["passable"]
- n_nstats = definition[p]["normal_stats"]
- n_astats = definition[p]["active_stats"]
- n_team = definition[p]["team"]
- n_equip = definition[p]["equipment"]
- np = Piece(n_sheet, n_sprite, n_anim, n_animated, self, n_name, n_pictures, n_passable, n_nstats, n_astats, n_team, n_equip)
- np.assign_tile(tuple(definition[p]["tile"]), self.bus.fetch("board_manager", "board_tile_layer")[tuple(definition[p]["tile"])])
+ # NOTE: This is one of those screwy things you don't often run into, but the copy module is
+ # needed here because of the pointer nature of python vals
+ work = copy.deepcopy(definition[p])
+ n_sheet = self.bus.fetch("sheet_manager", "sheets")[work["sheet"]]
+ n_anim = self.bus.fetch("sheet_manager", "animations")[work["sheet"]][work["animation"]]
+ n_name = work["name"]
+ n_pic = work["pictures"]
+ n_lvl = work["level"]
+ n_exp = work["exp"]
+ n_rank = work["rank"]
+ n_nstats = work["normal_stats"]
+ n_mod = work["stat_mod"]
+ n_dist = work["stat_dist"]
+ n_aff = work["affinity"]
+ n_tise = work["expertise"]
+ n_team = work["team"]
+ n_equip = work["equipment"]
+ n_growth = work["growth"]
+ np = Piece(n_sheet, (0, 0), n_anim, True, self, n_name, n_pic, n_lvl, n_exp, n_rank, n_aff, n_tise, False, n_nstats, n_mod, n_dist,
+ n_team, n_equip, n_growth)
+ np.assign_tile(tuple(work["tile"]), self.bus.fetch("board_manager", "board_tile_layer")[tuple(work["tile"])])
np.snap_to_tile()
self.add_piece(np)
def load_pieces_from_file(self, filename):
"""
- Load pieces from a given file. Checks to see if
- the provided filename is a JSON (or rather, has a
- .json extension) and tries to correct it if not.
- This method is called to load the pre-defined pieces
- that spawn in pre-made boards.
+ Load pieces from a given JSON file,
+ cross-referenced against the piece
+ database.
"""
+ # Setup
+ foldname = ""
+ full_def = {}
+
+ # Ensure the dir and file names match our structure
if filename[::-4] != ".json":
foldname = filename
filename = filename + ".json"
else:
foldname = filename[::-4]
+
+ # Next, load up the JSON file
jsondef = json.load(open(os.path.join(BOARD_PATH, foldname, filename)))
- self.load_pieces_from_def(jsondef)
+
+ # Now, create the full definition given the JSON and the character database
+ for p in jsondef:
+ temp = CHARBASE[jsondef[p]["template"]]
+ full_def[p] = {
+ "name" : jsondef[p]["name"],
+ "sheet" : jsondef[p]["replace"]["sheet"] if "sheet" in jsondef[p]["replace"].keys() else temp["sheet"],
+ "sprite" : (0, 0),
+ "animation" : jsondef[p]["replace"]["animation"] if "animation" in jsondef[p]["replace"].keys() else "stand_L",
+ "animated" : True,
+ "passable" : False,
+ "pictures" : jsondef[p]["replace"]["pictures"] if "pictures" in jsondef[p]["replace"].keys() else temp["pictures"],
+ "level" : jsondef[p]["level"],
+ "exp" : jsondef[p]["exp"],
+ "rank" : jsondef[p]["rank"],
+ "normal_stats" : temp["stats"],
+ "stat_mod" : jsondef[p]["stat_mod"],
+ "stat_dist" : jsondef[p]["stat_dist"],
+ "affinity" : jsondef[p]["replace"]["affinity"] if "affinity" in jsondef[p]["replace"] else temp["affinity"],
+ "expertise" : temp["expertise"],
+ "team" : jsondef[p]["team"],
+ "tile" : jsondef[p]["tile"],
+ "equipment" : jsondef[p]["equipment"],
+ "growth" : temp["growth"]
+ }
+
+ # Lastly, load the def as real pieces
+ self.load_pieces_from_def(full_def)
def load_tile_cursor(self, sheet):
"""
@@ -205,8 +245,8 @@ class Piece(entity.Entity):
"""
def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False,
- manager = None, name = None, pictures = None, passable = False, normal_stats = None, active_stats = None,
- team = None, equipment = None):
+ manager = None, name = None, pictures = None, level = None, exp = None, rank = None, affinity = None, expertise = None,
+ passable = False, normal_stats = None, stat_mod = None, stat_dist = None, team = None, equipment = None, growth = None):
# Parent initialization
super().__init__(sheet, sprite, animation, animated)
@@ -217,6 +257,11 @@ class Piece(entity.Entity):
# Others
self.manager = manager
self.name = name
+ self.level = 1
+ self.rank = rank
+ self.exp = exp
+ self.affinity = affinity
+ self.expertise = expertise
self.pictures = {
"icons_small" : None,
"icons_large" : None,
@@ -225,7 +270,11 @@ class Piece(entity.Entity):
self.load_pictures(pictures)
self.passable = passable
self.normal_stats = normal_stats
- self.active_stats = active_stats
+ self.active_stats = { i : 0 for i in self.normal_stats }
+ self.equip_mod = { i : 0 for i in self.normal_stats } # derived later
+ self.effect_mod = { i : 0 for i in self.normal_stats } # this mod can only be applied in battle, defaults empty always
+ self.dist_mod = { i : stat_dist[i] if i in stat_dist else 0 for i in self.normal_stats }
+ self.other_mod = { i : stat_mod[i] if i in stat_mod else 0 for i in self.normal_stats }
self.team = team
self.equipment = {
"weapon" : None,
@@ -234,8 +283,12 @@ class Piece(entity.Entity):
"acc2" : None,
"acc3" : None
}
+ self.growth = growth
self.attack_range = None
self.equip([equipment[e] for e in equipment])
+ if level != 1:
+ self.level_up(level)
+ self.modulate_stats()
self.back_to_stand = False # TODO: This may not be the best way
self.current_tile_path = []
self.current_tile_path_index = 0
@@ -269,7 +322,7 @@ class Piece(entity.Entity):
self.haste_mod = 3 # 3 - normal, 1 - minimum (slow 2), 5 - maximum (haste 2)
self.set_animation(self.manager.bus.fetch("sheet_manager", "animations")[self.sheet.name]["stand_" + self.facing.name], True)
-
+
def load_pictures(self, pictures):
"""
Load universal pictures from a given pictures
@@ -284,19 +337,18 @@ class Piece(entity.Entity):
that match entries in the item database.
"""
if type(equipment) == str:
- if (ITEMBASE[equipment]["slot"] != "weapon" or ITEMBASE[equipment]["type"] in self.active_stats["EXPERTISE"]):
+ if (ITEMBASE[equipment]["slot"] != "weapon" or ITEMBASE[equipment]["type"] in self.expertise):
self.equipment[ITEMBASE[equipment]["slot"]] = equipment
else:
print("Failed to equip " + equipment + " to " + self.name)
elif type(equipment) in (list, tuple):
for e in equipment:
if e != None:
- if ITEMBASE[e]["slot"] != "weapon" or ITEMBASE[e]["type"] in self.active_stats["EXPERTISE"]:
+ if ITEMBASE[e]["slot"] != "weapon" or ITEMBASE[e]["type"] in self.expertise:
self.equipment[ITEMBASE[e]["slot"]] = e
else:
print("Failed to equip " + e + " to " + self.name)
self.derive_range()
- self.derive_equipment_bonus()
def unequip(self, slot):
"""
@@ -320,15 +372,17 @@ class Piece(entity.Entity):
else:
self.attack_range = 0
- def derive_equipment_bonus(self):
+ def level_up(self, level_num):
"""
- Figure out what are stats are after taking the
- equipment into account.
+ Increase the level of this piece by 'level_num'
+ and take growth into account.
"""
- for i in self.equipment:
- if self.equipment[i] != None:
- for s in ITEMBASE[self.equipment[i]]["stats"]:
- self.active_stats[s] += ITEMBASE[self.equipment[i]]["stats"][s]
+ for l in range(self.level, min(self.level + level_num - 1, 21)):
+ self.level += 1
+ for g in self.growth:
+ if self.level % int(g) == 0:
+ for s in self.growth[g]:
+ self.normal_stats[s] += self.growth[g][s]
def render_damage_number(self):
"""
@@ -517,7 +571,9 @@ class Piece(entity.Entity):
if self.damage_timer == 72:
self.set_animation(self.manager.bus.fetch("sheet_manager", "animations")[self.sheet.name]["hurt_" + self.facing.name], True)
elif self.damage_timer == 30:
- self.active_stats["HP"] -= self.damage_to_receive
+ # TODO: This is possibly not the best way
+ self.effect_mod["HP"] -= self.damage_to_receive
+ self.modulate_stats()
if self.active_stats["HP"] > 0:
self.create_health_bar()
else:
@@ -535,6 +591,23 @@ class Piece(entity.Entity):
else:
self.set_animation(self.sheet.manager.animations[self.sheet.name]["stand_" + self.facing.name], True)
self.manager.bus.perform_turn_manager_refresh_state()
+
+ def modulate_stats(self):
+ """
+ Calculate each stat mod dict, and apply all
+ those mods to the normal_stats to produce the
+ current active_stats. This calculates only
+ the equip_mod; all other mods are determined
+ elsewhere and taken for granted here.
+ """
+ self.equip_mod = { i : 0 for i in self.normal_stats }
+ for e in self.equipment:
+ if self.equipment[e] != None:
+ for n in self.normal_stats:
+ self.equip_mod[n] += ITEMBASE[self.equipment[e]]["stats"][n]
+
+ for s in self.normal_stats:
+ self.active_stats[s] = self.normal_stats[s] + self.equip_mod[s] + self.other_mod[s] + self.dist_mod[s] + self.effect_mod[s]
def act(self):
"""
@@ -543,6 +616,7 @@ class Piece(entity.Entity):
self.execute_tile_path_move()
self.execute_attack_action()
self.execute_be_damaged()
+ self.modulate_stats()
# TODO: Something else should be done so that this doesn't overwrite other
# legit non-walking, non-standing anims. THIS MAY NOT BE THE BEST.
if self.back_to_stand: