Add Renpy Module

This commit is contained in:
DazedAnon 2024-11-14 08:04:47 -06:00
parent 2b82edc7b4
commit 10209e49ef
6 changed files with 625 additions and 180 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
*.yaml
*.yml
*.txt
*.rpy
!vocab.txt
!requirements.txt
*.csv

View file

@ -209,7 +209,7 @@ def translateKiriKiri(data, pbar, filename, jobList):
i = 0
# Regex
speakerRegex = r'【(.*)】\[CR\]'
speakerRegex = r"【(.*)】\[CR\]"
dialogueRegex = r"^\[text\](.*).*\[KeyWait\]|\[\w+\](.*)\[\/\w+\].*\[KeyWait\]"
furiganaRegex = r'(\[eruby\sstr="(.*?)"\stext.*?\])'
choicesRegex = r"^\s*\[button\d\sclickse=sys_decide.*text='(.*?)'.*"

View file

@ -50,6 +50,7 @@ from modules.wolf2 import handleWOLF2
from modules.javascript import handleJavascript
from modules.irissoft import handleIris
from modules.regex import handleRegex
from modules.renpy import handleRenpy
from modules.unity import handleUnity
from modules.images import handleImages
from modules.rpgmakerplugin import handlePlugin
@ -67,7 +68,7 @@ MODULES = [
["Eushully", ["txt"], handleEushully],
["Alice", ["txt"], handleAlice],
["Tyrano", ["ks"], handleTyrano],
["Kirikiri", ['ks', 'tjs', 'ssd', 'asd'], handleKirikiri],
["Kirikiri", ["ks", "tjs", "ssd", "asd"], handleKirikiri],
["JSON", ["json"], handleJSON],
["Kansen", ["ks"], handleKansen],
["Lune", ["json"], handleLune],
@ -79,6 +80,7 @@ MODULES = [
["Javascript", ["js"], handleJavascript],
["Iris", ["txt"], handleIris],
["Regex", ["txt"], handleRegex],
["Renpy", ["rpy"], handleRenpy],
["Unity", ["txt"], handleUnity],
["Images", [""], handleImages],
]
@ -133,7 +135,7 @@ files to translate are in the /files folder and that you picked the right game e
futures = [
executor.submit(MODULES[version][2], filename, estimate)
for filename in os.listdir("files")
for m in MODULES[version][1]
for m in MODULES[version][1]
if filename.endswith(m) and filename != ".gitkeep"
]
for future in as_completed(futures):

588
modules/renpy.py Normal file
View file

@ -0,0 +1,588 @@
# Libraries
import json
import os
import re
import textwrap
import threading
import time
import traceback
import tiktoken
import openai
from pathlib import Path
from colorama import Fore
from dotenv import load_dotenv
from retry import retry
from tqdm import tqdm
# Open AI
load_dotenv()
if os.getenv("api").replace(" ", "") != "":
openai.base_url = os.getenv("api")
openai.organization = os.getenv("org")
openai.api_key = os.getenv("key")
# Globals
MODEL = os.getenv("model")
TIMEOUT = int(os.getenv("timeout"))
LANGUAGE = os.getenv("language").capitalize()
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
THREADS = int(os.getenv("threads"))
LOCK = threading.Lock()
WIDTH = int(os.getenv("width"))
LISTWIDTH = int(os.getenv("listWidth"))
NOTEWIDTH = 70
MAXHISTORY = 10
ESTIMATE = ""
TOKENS = [0, 0]
NAMESLIST = []
NAMES = False # Output a list of all the character names found
BRFLAG = False # If the game uses <br> instead
FIXTEXTWRAP = True # Overwrites textwrap
IGNORETLTEXT = False # Ignores all translated text.
MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong)
FILENAME = None
# tqdm Globals
BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}"
POSITION = 0
LEAVE = False
PBAR = None
# Pricing - Depends on the model https://openai.com/pricing
# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request
# If you are getting a MISMATCH LENGTH error, lower the batch size.
if "gpt-3.5" in MODEL:
INPUTAPICOST = 0.002
OUTPUTAPICOST = 0.002
BATCHSIZE = 10
elif "gpt-4" in MODEL:
INPUTAPICOST = 0.0025
OUTPUTAPICOST = 0.01
BATCHSIZE = 40
def handleRenpy(filename, estimate):
global ESTIMATE
global FILENAME
FILENAME = filename
ESTIMATE = estimate
if ESTIMATE:
start = time.time()
translatedData = openFiles(filename)
# Print Result
end = time.time()
tqdm.write(getResultString(translatedData, end - start, filename))
with LOCK:
TOKENS[0] += translatedData[1][0]
TOKENS[1] += translatedData[1][1]
# Print Total
totalString = getResultString(["", TOKENS, None], end - start, "TOTAL")
# Print any errors on maps
if len(MISMATCH) > 0:
return (
totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET
)
else:
return totalString
else:
try:
with open(
"translated/" + filename, "w", encoding="utf8", errors="ignore"
) as outFile:
start = time.time()
translatedData = openFiles(filename)
# Print Result
end = time.time()
outFile.writelines(translatedData[0])
tqdm.write(getResultString(translatedData, end - start, filename))
with LOCK:
TOKENS[0] += translatedData[1][0]
TOKENS[1] += translatedData[1][1]
except Exception:
traceback.print_exc()
return "Fail"
return getResultString(["", TOKENS, None], end - start, "TOTAL")
def getResultString(translatedData, translationTime, filename):
# File Print String
totalTokenstring = (
Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]"
"[Output: "
+ str(translatedData[1][1])
+ "]" "[Cost: ${:,.4f}".format(
(translatedData[1][0] * 0.001 * INPUTAPICOST)
+ (translatedData[1][1] * 0.001 * OUTPUTAPICOST)
)
+ "]"
)
timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]"
if translatedData[2] == None:
# Success
return (
filename
+ ": "
+ totalTokenstring
+ timeString
+ Fore.GREEN
+ " \u2713 "
+ Fore.RESET
)
else:
# Fail
try:
raise translatedData[2]
except Exception as e:
traceback.print_exc()
errorString = str(e) + Fore.RED
return (
filename
+ ": "
+ totalTokenstring
+ timeString
+ Fore.RED
+ " \u2717 "
+ errorString
+ Fore.RESET
)
def openFiles(filename):
with open("files/" + filename, "r", encoding="utf8") as readFile:
translatedData = parseRenpy(readFile, filename)
# Delete lines marked for deletion
finalData = []
for line in translatedData[0]:
if line != "\\d\n":
finalData.append(line)
translatedData[0] = finalData
return translatedData
def parseRenpy(readFile, filename):
global PBAR
totalTokens = [0, 0]
# Read File into data
data = readFile.readlines()
# Create Progress Bar
with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
pbar.desc = filename
PBAR = pbar
try:
result = translateRenpy(data, [])
totalTokens[0] += result[0]
totalTokens[1] += result[1]
except Exception as e:
traceback.print_exc()
return [data, totalTokens, e]
return [data, totalTokens, None]
def translateRenpy(data, translatedList):
stringList = []
currentGroup = []
tokens = [0, 0]
speaker = ""
voice = False
global LOCK, ESTIMATE, FILENAME, PBAR
i = 0
while i < len(data):
voice = False
speaker = ""
lineRegexNoSpeaker = r'^\s\s\s\s"(.*)"'
lineRegexSpeaker = r'^\s\s\s\s(.+?)\s"(.*)"'
# Grab Line
match = re.search(lineRegexSpeaker, data[i])
if match:
response = getSpeaker(match.group(1))
jaString = match.group(2)
speaker = response[0]
tokens[0] += response[1][0]
tokens[1] += response[1][1]
else:
match = re.search(lineRegexNoSpeaker, data[i])
if match:
jaString = match.group(1)
# Valid Line
if match and 'voice' not in data[i]:
originalString = jaString
# Pass 1
if translatedList == []:
# Remove any textwrap
jaString = jaString.replace("\\n", " ")
# Add String
if speaker:
stringList.append(f"[{speaker}]: {jaString.strip()}")
else:
stringList.append(jaString.strip())
# Pass 2
else:
# Get Text
if translatedList:
# Grab and Pop
translatedText = translatedList[0]
translatedList.pop(0)
# Set to None if empty list
if len(translatedList) <= 0:
translatedList = None
# Remove speaker
if speaker != "":
matchSpeakerList = re.findall(
r"^\[?(.+?)\]?\s?[|:]\s?", translatedText
)
translatedText = re.sub(
r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText
)
# Escape Quotes
translatedText = re.sub(r'[\\]*(")', '\\"', translatedText)
translatedText = re.sub(r"[\\]*(')", "\\'", translatedText)
# Textwrap
translatedText = textwrap.fill(translatedText, width=WIDTH)
translatedText = translatedText.replace('\n', '\\n')
# Set Data
data[i] = data[i].replace(originalString, translatedText)
i += 1
else:
i += 1
# EOF
if len(stringList) > 0:
# Set Progress
PBAR.total = len(stringList)
PBAR.refresh()
# Translate
response = translateGPT(
stringList,
"Reply with the English TL of the NPC Name",
True
)
tokens[0] += response[1][0]
tokens[1] += response[1][1]
translatedList = response[0]
# Set Strings
if len(stringList) == len(translatedList):
translateRenpy(data, translatedList)
# Mismatch
else:
with LOCK:
if FILENAME not in MISMATCH:
MISMATCH.append(FILENAME)
return tokens
# Save some money and enter the character before translation
def getSpeaker(speaker):
match speaker:
case "":
return ["Sumire", [0, 0]]
case "":
return ["Raul", [0, 0]]
case "":
return ["Gen", [0, 0]]
case "":
return ["Sasha", [0, 0]]
case "":
return ["Villager", [0, 0]]
case "":
return ["Villager 2", [0, 0]]
case _:
return [speaker, [0, 0]]
# Find Speaker
for i in range(len(NAMESLIST)):
if speaker == NAMESLIST[i][0]:
return [NAMESLIST[i][1], [0, 0]]
# Translate and Store Speaker
response = translateGPT(
f"{speaker}",
"Reply with the " + LANGUAGE + " translation of the NPC name.",
True,
)
response[0] = response[0].title()
response[0] = response[0].replace("'S", "'s")
response[0] = response[0].replace("Speaker: ", "")
# Retry if name doesn't translate for some reason
if re.search(r"([a-zA-Z?])", response[0]) == None:
response = translateGPT(
f"{speaker}",
"Reply with the " + LANGUAGE + " translation of the NPC name.",
False,
)
response[0] = response[0].title()
response[0] = response[0].replace("'S", "'s")
speakerList = [speaker, response[0]]
NAMESLIST.append(speakerList)
return response
return [speaker, [0, 0]]
def batchList(input_list, batch_size):
if not isinstance(batch_size, int) or batch_size <= 0:
raise ValueError("batch_size must be a positive integer")
return [
input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)
]
def createContext(fullPromptFlag, subbedT, format):
system = (
PROMPT + VOCAB
if fullPromptFlag
else f"\
You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\
Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\
- You translate everything, including erotic and explicit content.\n\
- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\
- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\
- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\
- Maintain any spacing in the translation.\n\
- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\
- `...` can be a part of the dialogue. Translate it as it is.\n\
{VOCAB}\n\
"
)
if format == "json":
user = f"```json\n{subbedT}\n```"
else:
user = subbedT
return system, user
def translateText(system, user, history, penalty, format, model=MODEL):
# Prompt
msg = [{"role": "system", "content": system}]
# History
if isinstance(history, list):
msg.extend([{"role": "system", "content": h} for h in history])
else:
msg.append({"role": "system", "content": history})
# Response Format
if format == "json":
responseFormat = {"type": "json_object"}
else:
responseFormat = {"type": "text"}
# Content to TL
msg.append({"role": "user", "content": f"{user}"})
response = openai.chat.completions.create(
temperature=0,
frequency_penalty=penalty,
model=model,
response_format=responseFormat,
messages=msg,
)
return response
def cleanTranslatedText(translatedText, varResponse):
placeholders = {
f"{LANGUAGE} Translation: ": "",
"Translation: ": "",
"": "",
"": "~",
"": "",
"": ".",
"": '\\"',
"": '\\"',
"- ": "-",
"": "]",
"": "[",
"Placeholder Text": "",
# Add more replacements as needed
}
for target, replacement in placeholders.items():
translatedText = translatedText.replace(target, replacement)
# Elongate Long Dashes (Since GPT Ignores them...)
translatedText = elongateCharacters(translatedText)
return translatedText
def elongateCharacters(text):
# Define a pattern to match one character followed by one or more `ー` characters
# Using a positive lookbehind assertion to capture the preceding character
pattern = r"(?<=(.))ー+"
# Define a replacement function that elongates the captured character
def repl(match):
char = match.group(1) # The character before the ー sequence
count = len(match.group(0)) - 1 # Number of ー characters
return char * count # Replace ー sequence with the character repeated
# Use re.sub() to replace the pattern in the text
return re.sub(pattern, repl, text)
def extractTranslation(translatedTextList, is_list):
try:
translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList)
translatedTextList = re.sub(r"(?<![\\])\"+(?!\n)", r'"', translatedTextList)
line_dict = json.loads(translatedTextList)
# If it's a batch (i.e., list), extract with tags; otherwise, return the single item.
string_list = list(line_dict.values())
if is_list:
return string_list
else:
return string_list[0]
except Exception as e:
PBAR.write(f"extractTranslation Error: {e} on String {translatedTextList}")
return None
def countTokens(system, user, history):
inputTotalTokens = 0
outputTotalTokens = 0
enc = tiktoken.encoding_for_model("gpt-4")
# Input
if isinstance(history, list):
for line in history:
inputTotalTokens += len(enc.encode(line))
else:
inputTotalTokens += len(enc.encode(history))
inputTotalTokens += len(enc.encode(system))
inputTotalTokens += len(enc.encode(user))
# Output
outputTotalTokens += round(len(enc.encode(user)) * 3)
return [inputTotalTokens, outputTotalTokens]
def combineList(tlist, text):
if isinstance(text, list):
return [t for sublist in tlist for t in sublist]
return tlist[0]
@retry(exceptions=Exception, tries=5, delay=5)
def translateGPT(text, history, fullPromptFlag):
global PBAR, MISMATCH, FILENAME
with open("log/translationHistory.txt", "a+", encoding="utf-8") as logFile:
mismatch = False
totalTokens = [0, 0]
if isinstance(text, list):
format = "json"
tList = batchList(text, BATCHSIZE)
else:
format = "text"
tList = [text]
for index, tItem in enumerate(tList):
# Before sending to translation, if we have a list of items, add the formatting
if isinstance(tItem, list):
payload = {f"Line{i+1}": string for i, string in enumerate(tItem)}
payload = json.dumps(payload, indent=4, ensure_ascii=False)
varResponse = [payload, []]
subbedT = varResponse[0]
else:
varResponse = [tItem, []]
subbedT = varResponse[0]
# Things to Check before starting translation
if not re.search(
r"[一-龠ぁ-ゔァ-ヴーa---\uFF61-\uFF9F]+", subbedT
):
if PBAR is not None:
PBAR.update(len(tItem))
history = tItem[-MAXHISTORY:]
continue
# Create Message
system, user = createContext(fullPromptFlag, subbedT, format)
# Calculate Estimate
if ESTIMATE:
estimate = countTokens(system, user, history)
totalTokens[0] += estimate[0]
totalTokens[1] += estimate[1]
continue
# Translating
response = translateText(system, user, history, 0.05, format)
translatedText = response.choices[0].message.content
totalTokens[0] += response.usage.prompt_tokens
totalTokens[1] += response.usage.completion_tokens
# Check Translation
translatedText = cleanTranslatedText(translatedText, varResponse)
if isinstance(tItem, list):
extractedTranslations = extractTranslation(translatedText, True)
if extractedTranslations == None or len(tItem) != len(
extractedTranslations
):
# Mismatch. Try Again
response = translateText(
system, user, history, 0.05, format, "gpt-4o"
)
translatedText = response.choices[0].message.content
totalTokens[0] += response.usage.prompt_tokens
totalTokens[1] += response.usage.completion_tokens
# Formatting
translatedText = cleanTranslatedText(translatedText, varResponse)
if isinstance(tItem, list):
extractedTranslations = extractTranslation(translatedText, True)
if extractedTranslations == None or len(tItem) != len(
extractedTranslations
):
mismatch = True # Just here for breakpoint
logFile.write(f"Input:\n{subbedT}\n")
logFile.write(f"Output:\n{translatedText}\n")
# Set if no mismatch
if mismatch == False:
tList[index] = extractedTranslations
history = extractedTranslations[
-MAXHISTORY:
] # Update history if we have a list
else:
history = text[-MAXHISTORY:]
mismatch = False
if FILENAME not in MISMATCH:
MISMATCH.append(FILENAME)
# Update Loading Bar
with LOCK:
if PBAR is not None:
PBAR.update(len(tItem))
else:
# Ensure we're passing a single string to extractTranslation
tList[index] = translatedText
finalList = combineList(tList, text)
return [finalList, totalTokens]

View file

@ -37,11 +37,11 @@ MAXHISTORY = 10
ESTIMATE = ""
TOKENS = [0, 0]
NAMESLIST = []
FIRSTLINESPEAKERS = True # If 1st line of dialogue is a speaker, set to True
FIRSTLINESPEAKERS = False # If 1st line of dialogue is a speaker, set to True
NAMES = False # Output a list of all the character names found
BRFLAG = False # If the game uses <br> instead
FIXTEXTWRAP = True # Overwrites textwrap
IGNORETLTEXT = False # Ignores all translated text.
IGNORETLTEXT = True # Ignores all translated text.
MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong)
BRACKETNAMES = False
PBAR = None
@ -67,16 +67,16 @@ POSITION = 0
LEAVE = False
# Dialogue / Scroll / Choices (Main Codes)
CODE401 = True
CODE405 = True
CODE102 = True
CODE401 = False
CODE405 = False
CODE102 = False
# Optional
CODE101 = False # Turn this one when names exist in 101
CODE408 = False # Warning, translates comments and can inflate costs.
# Variables
CODE122 = False
CODE122 = True
# Other
CODE355655 = False
@ -938,6 +938,16 @@ def searchCodes(page, pbar, jobList, filename):
i += 1
continue
# Validate Japanese Text
if (
not re.search(
r"[一-龠ぁ-ゔァ-ヴーa---\uFF61-\uFF9F]+", jaString
)
and IGNORETLTEXT
):
i += 1
continue
# # For Retarded Devs
# retardRegex = r'([\\]+[nN]\[[\\]+V\[\d*?\]\])'
# match = re.search(retardRegex, jaString)
@ -1268,7 +1278,7 @@ def searchCodes(page, pbar, jobList, filename):
## Event Code: 122 [Set Variables]
if "code" in codeList[i] and codeList[i]["code"] == 122 and CODE122 is True:
# This is going to be the var being set. (IMPORTANT)
if codeList[i]["parameters"][0] not in list(range(155, 165)):
if codeList[i]["parameters"][0] not in list(range(0, 1000)):
i += 1
continue
@ -1291,7 +1301,7 @@ def searchCodes(page, pbar, jobList, filename):
# Set String
matchedText = None
if len(re.findall(r"([\'\"])", jaString)) == 2:
if len(re.findall(r"([\'\"\`])", jaString)) == 2:
matchedText = re.search(r"[\'\"\`](.*)[\'\"\`]", jaString)
# else:
# matchedText = re.search(r'(.*)', jaString)
@ -1323,9 +1333,7 @@ def searchCodes(page, pbar, jobList, filename):
translatedText = translatedText.replace("\n", "\\n")
# Set
codeList[i]["parameters"][4] = jaString.replace(
finalJAString, translatedText
)
codeList[i]["parameters"][4] = f"`{translatedText}`"
list122.pop(0)
## Event Code: 357 [Picture Text] [Optional]
@ -1494,6 +1502,7 @@ def searchCodes(page, pbar, jobList, filename):
if "TextPicture" in headerString or "BalloonInBattle" in headerString:
argVar = "text"
font = None
### Message Text First
if argVar in codeList[i]["parameters"][3]:
acExist = False
@ -1542,9 +1551,13 @@ def searchCodes(page, pbar, jobList, filename):
if acExist:
translatedText = f'\\ac {translatedText.replace('\n', '\n\\ac ')}'
# Check and Set Font
if "fontSize" in codeList[i]["parameters"][3]:
if font:
codeList[i]["parameters"][3]["fontSize"] = font
# Set
codeList[i]["parameters"][3][argVar] = translatedText
# codeList[i]["parameters"][3]['fontSize'] = "18"
list357.pop(0)
## Event Code: 657 [Picture Text] [Optional]
@ -2571,6 +2584,7 @@ def getSpeaker(speaker):
return response
return [speaker, [0, 0]]
def batchList(input_list, batch_size):
if not isinstance(batch_size, int) or batch_size <= 0:
raise ValueError("batch_size must be a positive integer")

170
vocab.txt
View file

@ -1,19 +1,10 @@
Here are some vocabulary and terms so that you know the proper spelling and translation.
```
# Game Characters
レイア (Leia) - Female
アウラ (Aura) - Female
ハヤト (Hayato) - Male
ルード (Rude) - Male
トマ (Toma) - Male
ガスト (Gust) - Male
マスル (Masuru) - Male
準騎士 (Junior Knight) - Male
カグヤ (Kaguya) - Unknown
クローネ (Krone) - Male
レックス (Rex) - Male
アイカ (Aika) - Unknown
トミー (Tommy) - Male
スミレ (Sumire) - Female
サシャ (Sasha) - Female
ラウル (Raul) - Male
ゲン (Gen) - Male
# Lewd Terms
マンコ (pussy)
@ -90,157 +81,6 @@ ME 音量 (ME Volume)
w ((lol))
巫女 (Shrine Maiden)
コイツ (this bastard)
シュード (Schnood)
# Locations
真エンド (True End)
ドリンク&ベーカリー (Drink & Bakery)
喫茶クナイ (Cafe Kunai)
ミクダース民家 (Mikudas Residence)
ブラウン探偵事務所 (Brown Detective Agency)
ル王都 (Capital of Ru)
2ママワガ別荘 (2 Mama Waga Villa)
ルノースオーラ城レイア自室 (Lunorth Aura Castle Leia's Room)
エトワール (Etoile)
シュード地区 (Shude District)
レポロ (Reporo)
コル (Kor)
ステゴ (Stego)
リスカ (Riska)
#Armors
-----鎧系 (-----Armor Types)
布の服 (Cloth Clothes)
レザーベスト (Leather Vest)
冒険者の服 (Adventurer's Clothes)
ハードレザー (Hard Leather)
ブリガンダイン (Brigandine)
クロースアーマー (Cloth Armor)
レザーアーマー (Leather Armor)
ブロンズブレスト (Bronze Breastplate)
アイアンブレスト (Iron Breastplate)
ミスリルブレスト (Mithril Breastplate)
ドラゴンブレスト (Dragon Breastplate)
ブロンズアーマー (Bronze Armor)
アイアンアーマー (Iron Armor)
ミスリルアーマー (Mithril Armor)
ドラゴンアーマー (Dragon Armor)
木綿のローブ (Cotton Robe)
絹のマント (Silk Mantle)
術士のローブ (Sorcerer's Robe)
賢者のローブ (Sage's Robe)
エレメンタルマント (Elemental Mantle)
-----盾系 (-----Shield Types)
バックラー (Buckler)
ラウンドバックラー (Round Buckler)
スパイクバックラー (Spike Buckler)
ミスリルバックラー (Mithril Buckler)
ドラゴンバックラー (Dragon Buckler)
ウッドシールド (Wood Shield)
ブロンズシールド (Bronze Shield)
アイアンシールド (Iron Shield)
ミスリルシールド (Mithril Shield)
ドラゴンシールド (Dragon Shield)
ウッドバングル (Wood Bangle)
ブロンズバングル (Bronze Bangle)
アイアンバングル (Iron Bangle)
ミスリルバングル (Mithril Bangle)
ドラゴンバングル (Dragon Bangle)
-----兜系 (-----Helmets)
バンダナ (Bandana)
レザーバンダナ (Leather Bandana)
皮の帽子 (Leather Hat)
ターバン (Turban)
羽根付き帽子 (Feathered Hat)
レザーキャップ (Leather Cap)
ウッドキャップ (Wood Cap)
ブロンズキャップ (Bronze Cap)
アイアンキャップ (Iron Cap)
ミスリルキャップ (Mithril Cap)
ドラゴンキャップ (Dragon Cap)
チェインコイフ (Chain Coif)
ブロンズヘルム (Bronze Helm)
アイアンヘルム (Iron Helm)
ミスリルヘルム (Mithril Helm)
ドラゴンヘルム (Dragon Helm)
とんがり帽子 (Pointed Hat)
サークレット (Circlet)
シルバーサークレット (Silver Circlet)
ゴールドサークレット (Gold Circlet)
ミスリルサークレット (Mithril Circlet)
マスターサークレット (Master Circlet)
-----装飾品系 (-----Accessories)
ガードストーン (Guard Stone)
ガードリング (Guard Ring)
パワーストーン (Power Stone)
パワーリング (Power Ring)
スピードストーン (Speed Stone)
スピードリング (Speed Ring)
ラッキーストーン (Lucky Stone)
ラッキーリング (Lucky Ring)
マジカルストーン (Magical Stone)
マジカルリング (Magical Ring)
シューターストーン (Shooter Stone)
シューターリング (Shooter Ring)
マスターストーン (Master Stone)
マスターリング (Master Ring)
ポイズンガード (Poison Guard)
ポイズンガードS (Poison Guard S)
メンタルガード (Mental Guard)
メンタルガードS (Mental Guard S)
(TP+)
エネミーレーダー (Enemy Radar)
ワーニングベル (Warning Bell)
TPストッカー (TP Stocker)
ガッツリング (Guts Ring)
MPキーパー (MP Keeper)
エレメンタルガード (Elemental Guard)
スペクタクル (Spectacle)
#Enemies
ゴブリン (Goblin)
ノーム (Gnome)
クロウ (Crow)
トレント (Treant)
魔犬 (Hellhound)
バットン (Batton)
山賊 (Bandit)
ハッチン (Hatchin)
女王ハッチン (Queen Hatchin)
騎士 (Knight)
赤騎士 (Red Knight)
パンダン (Pandan)
キノコン (Kinokon)
マンドレ (Mandre)
トレントン (Trenton)
レッドラン (Redran)
サイクロップ (Cyclops)
ハーピィ (Harpy)
ミノタウロス (Minotaur)
強ミノタウロス (Strong Minotaur)
暴漢 (Thug)
ゴブゴブリン (Gobgoblin)
レジスタンス (Resistance)
騎士 (Knight)
バイバットン (Bybatton)
悪魔犬 (Demon Dog)
マキシマムシ (Maximum Bug)
ストーカー (Stalker)
ナナハッチン (Nanahatchin)
スライン (Sline)
ミミックン (Mimicun)
ドットレントン (Dot Renton)
ブタン (Butan)
ミツハッチン (Mitsuhatchin)
野盗 (Bandit)
鎧の男 (Armored Man)
ボスオークン (Boss Orkun)
オークン (Orkun)
シュードの男 (Shude Man)
ボスネークン (Boss Snakun)
コスネークン (Kosnakun)
グレートパンダン (Great Pandan)
テスト (Test)
ゴブリソ (Goblinso)
ボスネークン (Boss Snakun)
復讐者 (Avenger)
```