230 lines
8.7 KiB
Plaintext
230 lines
8.7 KiB
Plaintext
|
###############################################################################################################
|
||
|
### LINT+ REN'PY WORD COUNTER ################################################################################
|
||
|
###############################################################################################################
|
||
|
#
|
||
|
# Thanks for downloading Lint+! ( https://kigyo.itch.io/renpy-word-counter )
|
||
|
# Below are a bunch of variables that let you customize the generated output according to your needs.
|
||
|
#
|
||
|
# If you like this tool, consider dropping a donation: https://ko-fi.com/kigyodev
|
||
|
# Paying just $1 on itch.io also lets you access an additional feature: label-based statistics!
|
||
|
#
|
||
|
# Thank you, and may this tool help track your progress better than ever! :D
|
||
|
# - KigyoDev
|
||
|
|
||
|
## Preferences: Counters ################################################################################################
|
||
|
|
||
|
# Line and word counts for every character
|
||
|
# default value: True
|
||
|
define wordcounter_characters = True
|
||
|
|
||
|
# Line and word counts for each .rpy file
|
||
|
# default value: True
|
||
|
define wordcounter_files = True
|
||
|
|
||
|
# Line and word counts for every character, within each .rpy file
|
||
|
# default value: True
|
||
|
define wordcounter_character_files = True
|
||
|
|
||
|
# Number of menus and available choices in the game
|
||
|
# default value: True
|
||
|
define wordcounter_menu_choices = True
|
||
|
|
||
|
# Show character/symbol counts, which might be too much detail - otherwise only displays number of dialogue blocks and words
|
||
|
# default value: False
|
||
|
define wordcounter_display_character_count = False
|
||
|
|
||
|
# DONATE TO UNLOCK:
|
||
|
# Line and word counts for every label
|
||
|
# default value: True
|
||
|
define wordcounter_labels = True
|
||
|
|
||
|
|
||
|
# The old character statistics which only displays line counts
|
||
|
# default value: False
|
||
|
define config.lint_character_statistics = False
|
||
|
|
||
|
## Preferences: File paths ################################################################################################
|
||
|
|
||
|
# Name of the folder your script files are in, if any. This can make the generated output look nicer.
|
||
|
# Example: "script/"
|
||
|
# default value: ""
|
||
|
define script_folder_path = ""
|
||
|
|
||
|
# List of folders and files you want to ignore.
|
||
|
# Example: if you want to make sure nothing in the "unused" folder and the "script.rpy" file is counted, write ["unused", "script.rpy"]
|
||
|
# Note: Be careful with unintentionally matching filenames! Ignoring "no" will also ignore any folders and file names containing "no".
|
||
|
# default value: []
|
||
|
define script_ignore_path = []
|
||
|
|
||
|
|
||
|
## Preferences: Characters ################################################################################################
|
||
|
|
||
|
# List of equivalent characters that should be counted as one. Given ("x", "y"), "y" will be counted as "x".
|
||
|
# Example: if "ann", "xann", and "nann" should all be considered "ann", write [("ann", "xann"), ("ann", "nann")]
|
||
|
# default value: []
|
||
|
define wordcounter_same = []
|
||
|
|
||
|
# List of characters who should be hidden from the character statistics. They still contribute to the total file word count.
|
||
|
# default value: ["extend"]
|
||
|
define wordcounter_hidden = ["extend"]
|
||
|
|
||
|
# List of characters who should not count towards any word counts.
|
||
|
# default value: []
|
||
|
define wordcounter_uncounted = []
|
||
|
|
||
|
|
||
|
###############################################################################################################
|
||
|
### The Code ##################################################################################################
|
||
|
###############################################################################################################
|
||
|
|
||
|
init python:
|
||
|
|
||
|
import collections
|
||
|
|
||
|
# The main function
|
||
|
def wordcounter():
|
||
|
|
||
|
all_stmts = list(renpy.game.script.all_stmts)
|
||
|
all_stmts.sort(key=lambda n : n.filename)
|
||
|
|
||
|
charastats = collections.defaultdict(Count)
|
||
|
filestats = collections.defaultdict(Count)
|
||
|
filecharastats = {}
|
||
|
|
||
|
menu_count = 0
|
||
|
options_count = 0
|
||
|
|
||
|
unignored_name = "game/" + script_folder_path + "every file combined"
|
||
|
|
||
|
filecharastats[unignored_name] = collections.defaultdict(Count)
|
||
|
|
||
|
for node in all_stmts:
|
||
|
if isinstance(node, renpy.ast.Say):
|
||
|
speaker = node.who
|
||
|
for i in wordcounter_same:
|
||
|
if i[1] == speaker:
|
||
|
speaker = i[0]
|
||
|
break
|
||
|
|
||
|
if not_ignored_path(node.filename) and speaker not in wordcounter_uncounted:
|
||
|
filestats[unignored_name].add(node.what)
|
||
|
filestats[node.filename].add(node.what)
|
||
|
|
||
|
if node.filename not in filecharastats:
|
||
|
filecharastats[node.filename] = collections.defaultdict(Count)
|
||
|
|
||
|
if speaker not in wordcounter_hidden:
|
||
|
charastats[speaker if speaker else 'narrator' ].add(node.what)
|
||
|
filecharastats[node.filename][speaker if speaker else 'narrator' ].add(node.what)
|
||
|
|
||
|
elif isinstance(node, renpy.ast.Menu):
|
||
|
menu_count += 1
|
||
|
for l, c, b in node.items:
|
||
|
options_count += 1
|
||
|
|
||
|
if renpy.config.developer and wordcounter_characters:
|
||
|
print("\n")
|
||
|
report_character_stats(charastats)
|
||
|
|
||
|
if renpy.config.developer and wordcounter_files:
|
||
|
print("\n")
|
||
|
report_file_stats(filestats)
|
||
|
|
||
|
if renpy.config.developer and wordcounter_character_files:
|
||
|
print("\n")
|
||
|
report_file_chara_stats(filestats, filecharastats)
|
||
|
|
||
|
if renpy.config.developer and wordcounter_menu_choices:
|
||
|
print("\n")
|
||
|
report_menu_stats(menu_count, options_count)
|
||
|
|
||
|
# This makes sure the above function is actually called whenever you use Lint
|
||
|
config.lint_hooks.append(wordcounter)
|
||
|
|
||
|
def not_ignored_path(filename):
|
||
|
for i in script_ignore_path:
|
||
|
if i in filename:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
# The print functions:
|
||
|
def report_character_stats(charastats, title = True):
|
||
|
|
||
|
if title:
|
||
|
print("Character statistics:")
|
||
|
|
||
|
count_to_char = collections.defaultdict(list)
|
||
|
|
||
|
for char in charastats:
|
||
|
count_to_char[charastats[char].blocks].append(char)
|
||
|
|
||
|
for count, chars in sorted(count_to_char.items(), reverse=True):
|
||
|
chars.sort()
|
||
|
|
||
|
if len(chars) == 1:
|
||
|
start = chars[0] + " has "
|
||
|
end = humanize(charastats[chars[0]].words)
|
||
|
elif len(chars) == 2:
|
||
|
start = chars[0] + " and " + chars[1] + " have "
|
||
|
end = humanize(charastats[chars[0]].words) + " and " + humanize(charastats[chars[1]].words)
|
||
|
else:
|
||
|
start = ", ".join(chars[:-1]) + ", and " + chars[-1] + " have "
|
||
|
end = ""
|
||
|
for char in chars[:-1]:
|
||
|
end += humanize(charastats[char].words) + ", "
|
||
|
end += "and " + humanize(charastats[chars[-1]].words)
|
||
|
|
||
|
print(" * " + start + humanize(count) +
|
||
|
(" block" if count == 1 else " blocks") + " of dialogue, and " +
|
||
|
end + " words" + (" each." if len(chars) > 1 else ".") )
|
||
|
|
||
|
def report_file_stats(filestats):
|
||
|
|
||
|
print("File statistics:")
|
||
|
|
||
|
count_to_char = collections.defaultdict(list)
|
||
|
|
||
|
for file in filestats:
|
||
|
print(" * [" + file[5+len(script_folder_path):] + "] contains " + humanize(filestats[file].blocks) +
|
||
|
" dialogue blocks and " + humanize(filestats[file].words) + " words.")
|
||
|
|
||
|
def report_file_chara_stats(filestats, filecharastats):
|
||
|
|
||
|
print("Detailed File statistics:")
|
||
|
|
||
|
count_to_char = collections.defaultdict(list)
|
||
|
|
||
|
for file in filestats:
|
||
|
print("[" + file[5+len(script_folder_path):] + "] contains " + humanize(filestats[file].blocks) +
|
||
|
" dialogue blocks and " + humanize(filestats[file].words) + " words:")
|
||
|
report_character_stats(filecharastats[file], False)
|
||
|
print("")
|
||
|
|
||
|
def report_menu_stats(menu_count, options_count):
|
||
|
|
||
|
print("Menu statistics:")
|
||
|
|
||
|
print("The game has " + str(menu_count) + " menus, with a total of " + str(options_count) + " possible choices, \nfor an average of " +
|
||
|
"{:,.2f}".format(options_count and options_count/menu_count or 0) + " choices per menu.")
|
||
|
|
||
|
# Auxiliary functions directly copied from lint.py - I take no credit for these:
|
||
|
def humanize(n):
|
||
|
s = str(n)
|
||
|
rv = []
|
||
|
for i, c in enumerate(reversed(s)):
|
||
|
if i and not (i % 3):
|
||
|
rv.insert(0, ',')
|
||
|
rv.insert(0, c)
|
||
|
return ''.join(rv)
|
||
|
|
||
|
class Count(object):
|
||
|
def __init__(self):
|
||
|
self.blocks = 0
|
||
|
self.words = 0
|
||
|
self.characters = 0
|
||
|
|
||
|
def add(self, s):
|
||
|
self.blocks += 1
|
||
|
self.words += len(s.split())
|
||
|
self.characters += len(s)
|