Changes to wolf and MV
This commit is contained in:
parent
dd472cb89f
commit
1ed64f2e56
4 changed files with 169 additions and 109 deletions
|
|
@ -192,7 +192,7 @@ def getImageDimensions(file_path):
|
|||
def processImagesDir(directory_path, imageList):
|
||||
for file_name in os.listdir(directory_path):
|
||||
# .png and Japanese
|
||||
if '.png' in file_name and re.search(r'[一-龠ぁ-ゔァ-ヴー]+', file_name) and file_name.replace('.png', '') in VOCAB:
|
||||
if '.png' in file_name and file_name.replace('.png', '') in VOCAB:
|
||||
file_path = os.path.join(directory_path, file_name)
|
||||
if os.path.isfile(file_path):
|
||||
# Check if the file is an image
|
||||
|
|
|
|||
|
|
@ -60,11 +60,11 @@ LEAVE = False
|
|||
|
||||
# Dialogue / Scroll
|
||||
CODE401 = True
|
||||
CODE405 = False
|
||||
CODE405 = True
|
||||
CODE408 = False
|
||||
|
||||
# Choices
|
||||
CODE102 = False
|
||||
CODE102 = True
|
||||
|
||||
# Variables
|
||||
CODE122 = False
|
||||
|
|
@ -488,7 +488,10 @@ def searchNames(data, pbar, context):
|
|||
tokensResponse = translateNote(data[i], r'<note:(.*?)>')
|
||||
totalTokens[0] += tokensResponse[0]
|
||||
totalTokens[1] += tokensResponse[1]
|
||||
|
||||
if 'PE拡張' in data[i]['note']:
|
||||
tokensResponse = translateNote(data[i], r'<PE拡張:(.*?)>')
|
||||
totalTokens[0] += tokensResponse[0]
|
||||
totalTokens[1] += tokensResponse[1]
|
||||
i += 1
|
||||
else:
|
||||
batchFull = True
|
||||
|
|
@ -611,7 +614,9 @@ def searchNames(data, pbar, context):
|
|||
else:
|
||||
# Get Text
|
||||
if data[j]['name'] != '':
|
||||
data[j]['name'] = translatedNameBatch[0]
|
||||
with open('translations.txt', 'a', encoding='utf-8') as file:
|
||||
file.write(f'{data[j]['name']} ({translatedNameBatch[0]})\n')
|
||||
data[j]['name'] = translatedNameBatch[0]
|
||||
translatedNameBatch.pop(0)
|
||||
if data[j]['nickname'] != '':
|
||||
data[j]['nickname'] = translatedNicknameBatch[0]
|
||||
|
|
@ -644,26 +649,29 @@ def searchNames(data, pbar, context):
|
|||
# Set Data
|
||||
if len(nameList) == len(translatedNameBatch):
|
||||
j = k
|
||||
while j < i:
|
||||
# Empty Data
|
||||
if data[j] is None or data[j]['name'] == "":
|
||||
j += 1
|
||||
continue
|
||||
else:
|
||||
# Get Text
|
||||
data[j]['name'] = translatedNameBatch[0]
|
||||
if 'description' in data[j]:
|
||||
data[j]['description'] = textwrap.fill(translatedDescriptionBatch[0], LISTWIDTH)
|
||||
translatedNameBatch.pop(0)
|
||||
translatedDescriptionBatch.pop(0)
|
||||
with open('translations.txt', 'a', encoding='utf-8') as file:
|
||||
file.write(f'# Items\n')
|
||||
while j < i:
|
||||
# Empty Data
|
||||
if data[j] is None or data[j]['name'] == "":
|
||||
j += 1
|
||||
continue
|
||||
else:
|
||||
# Get Text
|
||||
file.write(f'{data[j]['name']} ({translatedNameBatch[0]})\n')
|
||||
data[j]['name'] = translatedNameBatch[0]
|
||||
if 'description' in data[j]:
|
||||
data[j]['description'] = textwrap.fill(translatedDescriptionBatch[0], LISTWIDTH)
|
||||
translatedNameBatch.pop(0)
|
||||
translatedDescriptionBatch.pop(0)
|
||||
|
||||
# If Batch is empty. Move on.
|
||||
if len(translatedNameBatch) == 0:
|
||||
nameList.clear()
|
||||
descriptionList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
j += 1
|
||||
# If Batch is empty. Move on.
|
||||
if len(translatedNameBatch) == 0:
|
||||
nameList.clear()
|
||||
descriptionList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
j += 1
|
||||
else:
|
||||
mismatch = True
|
||||
if context in ['Enemies', 'Classes', 'MapInfos']:
|
||||
|
|
@ -720,14 +728,12 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
list355655 = []
|
||||
list108 = []
|
||||
setData = False
|
||||
currentGroup = []
|
||||
textHistory = []
|
||||
match = []
|
||||
totalTokens = [0, 0]
|
||||
translatedText = ''
|
||||
speaker = ''
|
||||
speakerID = None
|
||||
nametag = ''
|
||||
syncIndex = 0
|
||||
CLFlag = False
|
||||
maxHistory = MAXHISTORY
|
||||
|
|
@ -761,6 +767,10 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
if len(codeList) <= i:
|
||||
break
|
||||
|
||||
# Declare Varss
|
||||
currentGroup = []
|
||||
nametag = ''
|
||||
|
||||
## Event Code: 401 Show Text
|
||||
if 'code' in codeList[i] and codeList[i]['code'] in [401, 405, -1] and (CODE401 or CODE405):
|
||||
# Save Code and starting index (j)
|
||||
|
|
@ -810,8 +820,15 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
if len(speakerList) == 0:
|
||||
speakerList = re.findall(r'^[\\]+[cC]\[[\d]+\](.+?)[\\]+[Cc]\[[\d]\]\\?\\?$', jaString)
|
||||
|
||||
# None
|
||||
# First Line Speakers
|
||||
if len(speakerList) == 0 and FIRSTLINESPEAKERS is True:
|
||||
# Remove any RPGMaker Code at start
|
||||
ffMatch = re.search(r'^(\s*[\\]+[aAbBdDeEfFgGhHiIjJlLmMoOpPqQrRsStTuUvVwWxXyYzZ]+\[[\w\d\[\]\\]+\])', jaString)
|
||||
if ffMatch != None:
|
||||
jaString = jaString.replace(ffMatch.group(0), '')
|
||||
nametag += ffMatch.group(0)
|
||||
|
||||
# Test Speaker
|
||||
if len(jaString) < 40 \
|
||||
and 'code' in codeList[i+1] \
|
||||
and codeList[i+1]['code'] in [401, 405, -1] \
|
||||
|
|
@ -828,7 +845,8 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
totalTokens[1] += response[1][1]
|
||||
|
||||
# Set Data
|
||||
codeList[i]['parameters'][0] = jaString.replace(speakerList[0], speaker)
|
||||
codeList[i]['parameters'][0] = nametag + jaString.replace(speakerList[0], speaker)
|
||||
nametag = ''
|
||||
|
||||
# Iterate to next string
|
||||
i += 1
|
||||
|
|
@ -861,7 +879,8 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
|
||||
# Format String
|
||||
if len(currentGroup) > 0:
|
||||
finalJAString = ' '.join(currentGroup).replace('?', '?')
|
||||
finalJAString = ''
|
||||
finalJAString = ''.join(currentGroup).replace('?', '?')
|
||||
oldjaString = finalJAString
|
||||
|
||||
# Check if Empty
|
||||
|
|
@ -956,7 +975,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
finalJAString = finalJAString.replace(match, '')
|
||||
|
||||
# Remove any RPGMaker Code at start
|
||||
ffMatch = re.search(r'^([.\\]+[aAbBcCdDeEfFgGhHiIjJlLmMoOpPqQrRsStTuUvVwWxXyYzZ]+\[.+?\]\]?)+', finalJAString)
|
||||
ffMatch = re.search(r'^(\s*[\\]+[aAbBdDeEfFgGhHiIjJlLmMoOpPqQrRsStTuUvVwWxXyYzZ]+\[[\w\d\[\]\\]+\])', finalJAString)
|
||||
if ffMatch != None:
|
||||
finalJAString = finalJAString.replace(ffMatch.group(0), '')
|
||||
nametag += ffMatch.group(0)
|
||||
|
|
@ -997,6 +1016,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
list401.append(speaker)
|
||||
speaker = ''
|
||||
match = []
|
||||
nametag = ''
|
||||
currentGroup = []
|
||||
syncIndex = i + 1
|
||||
|
||||
|
|
@ -2136,20 +2156,6 @@ def batchList(input_list, batch_size):
|
|||
return [input_list[i:i + batch_size] for i in range(0, len(input_list), batch_size)]
|
||||
|
||||
def createContext(fullPromptFlag, subbedT, format):
|
||||
characters = 'Game Characters:\n\
|
||||
圭太 (Keita) - Male\n\
|
||||
涼香 (Ryoka) - Female\n\
|
||||
咲 (Saki) - Female\n\
|
||||
大介 (Daisuke) - Male\n\
|
||||
浮浪者 (Vagrant) - Male\n\
|
||||
まさる (Masaru) - Male\n\
|
||||
ノブオ (Nobuo) - Male\n\
|
||||
安井 (Yasui) - Male\n\
|
||||
大山 (Oyama) - Male\n\
|
||||
関口 (Sekiguchi) - Male\n\
|
||||
黒崎 (Kurosaki) - Male\n\
|
||||
'
|
||||
|
||||
system = PROMPT + VOCAB if fullPromptFlag else \
|
||||
f"\
|
||||
You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\
|
||||
|
|
@ -2167,15 +2173,12 @@ Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{
|
|||
user = f'```json\n{subbedT}\n```'
|
||||
else:
|
||||
user = subbedT
|
||||
return characters, system, user
|
||||
return system, user
|
||||
|
||||
def translateText(characters, system, user, history, penalty, format, model=MODEL):
|
||||
def translateText(system, user, history, penalty, format, model=MODEL):
|
||||
# Prompt
|
||||
msg = [{"role": "system", "content": system}]
|
||||
|
||||
# Characters
|
||||
msg.append({"role": "system", "content": characters})
|
||||
|
||||
# History
|
||||
if isinstance(history, list):
|
||||
msg.extend([{"role": "system", "content": h} for h in history])
|
||||
|
|
@ -2250,7 +2253,7 @@ def extractTranslation(translatedTextList, is_list):
|
|||
return None
|
||||
|
||||
|
||||
def countTokens(characters, system, user, history):
|
||||
def countTokens(system, user, history):
|
||||
inputTotalTokens = 0
|
||||
outputTotalTokens = 0
|
||||
enc = tiktoken.encoding_for_model('gpt-4')
|
||||
|
|
@ -2262,7 +2265,6 @@ def countTokens(characters, system, user, history):
|
|||
else:
|
||||
inputTotalTokens += len(enc.encode(history))
|
||||
inputTotalTokens += len(enc.encode(system))
|
||||
inputTotalTokens += len(enc.encode(characters))
|
||||
inputTotalTokens += len(enc.encode(user))
|
||||
|
||||
# Output
|
||||
|
|
@ -2306,17 +2308,17 @@ def translateGPT(text, history, fullPromptFlag):
|
|||
continue
|
||||
|
||||
# Create Message
|
||||
characters, system, user = createContext(fullPromptFlag, subbedT, format)
|
||||
system, user = createContext(fullPromptFlag, subbedT, format)
|
||||
|
||||
# Calculate Estimate
|
||||
if ESTIMATE:
|
||||
estimate = countTokens(characters, system, user, history)
|
||||
estimate = countTokens(system, user, history)
|
||||
totalTokens[0] += estimate[0]
|
||||
totalTokens[1] += estimate[1]
|
||||
continue
|
||||
|
||||
# Translating
|
||||
response = translateText(characters, system, user, history, 0.05, format)
|
||||
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
|
||||
|
|
@ -2327,7 +2329,7 @@ def translateGPT(text, history, fullPromptFlag):
|
|||
extractedTranslations = extractTranslation(translatedText, True)
|
||||
if extractedTranslations == None or len(tItem) != len(extractedTranslations):
|
||||
# Mismatch. Try Again
|
||||
response = translateText(characters, system, user, history, 0.05, format, 'gpt-4o')
|
||||
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
|
||||
|
|
|
|||
109
modules/wolf.py
109
modules/wolf.py
|
|
@ -62,7 +62,7 @@ FILENAME = None
|
|||
# Dialogue / Scroll
|
||||
CODE101 = False
|
||||
CODE102 = False
|
||||
CODE122 = False
|
||||
CODE122 = True
|
||||
|
||||
# Other
|
||||
CODE210 = False
|
||||
|
|
@ -468,9 +468,10 @@ def searchCodes(events, pbar, jobList, filename):
|
|||
codeList[i]['stringArgs'][0] = translatedText
|
||||
|
||||
### Event Code: 300 Common Events
|
||||
if codeList[i]['code'] == 300 and CODE300 == True and len(codeList[i]['stringArgs']) > 0:
|
||||
if codeList[i]['code'] == 300 and CODE300 == True and 'stringArgs' in codeList[i] and len(codeList[i]['stringArgs']) > 1:
|
||||
# Choices
|
||||
if codeList[i]['stringArgs'][0] == "[共]汎用ウィンドウ生成":
|
||||
if codeList[i]['stringArgs'][0] == "[共]汎用ウィンドウ生成" \
|
||||
or codeList[i]['stringArgs'][0] == "[共]選択生成":
|
||||
# Grab String
|
||||
choiceList = codeList[i]['stringArgs'][1].split('\r\n')
|
||||
|
||||
|
|
@ -502,54 +503,56 @@ def searchCodes(events, pbar, jobList, filename):
|
|||
elif codeList[i]['stringArgs'][0] == "Hメッセージ" \
|
||||
or codeList[i]['stringArgs'][0] == 'Hしらべる' \
|
||||
or codeList[i]['stringArgs'][0] == '[共]Hメッセージ+' \
|
||||
or codeList[i]['stringArgs'][0] == '[共]ポップアップ表示' \
|
||||
or codeList[i]['stringArgs'][0] == 'd[共]ポップアップ表示' \
|
||||
or codeList[i]['stringArgs'][0] == 'm_謎冒頭' \
|
||||
or (codeList[i]['codeStr'] == 'SetString' and '\n' in codeList[i]['stringArgs'][0])\
|
||||
or (codeList[i]['codeStr'] == 'SetString' and ('「' in codeList[i]['stringArgs'][0] or '*' in codeList[i]['stringArgs'][0]))\
|
||||
or codeList[i]['stringArgs'][0] == '[移]サウンドノベル':
|
||||
if len(codeList[i]['stringArgs']) > 1:
|
||||
cleanedList = None
|
||||
if len(codeList[i]['stringArgs']) > 1 and not re.search(r'^[\\]+cself\[\d+\]$', codeList[i]['stringArgs'][1]):
|
||||
cleanedList = formatDramon(codeList[i]['stringArgs'][1])
|
||||
else:
|
||||
elif codeList[i]['code'] == 122:
|
||||
cleanedList = formatDramon(codeList[i]['stringArgs'][0])
|
||||
fontSize = 24
|
||||
translatedText = ''
|
||||
if cleanedList:
|
||||
fontSize = 24
|
||||
translatedText = ''
|
||||
|
||||
for str in cleanedList:
|
||||
# Pass 1
|
||||
if not setData:
|
||||
if all(x not in str for x in ['_', '@', '>', '/']) and str != '\r\n':
|
||||
# Remove Textwrap and Font and Add to list
|
||||
str = str.replace('\r\n', ' ')
|
||||
str = str.replace(f'\\f[{fontSize}]', '')
|
||||
list300.append(str)
|
||||
for str in cleanedList:
|
||||
# Pass 1
|
||||
if not setData:
|
||||
if all(x not in str for x in ['_', '@', '>', '/']) and str != '\r\n':
|
||||
# Remove Textwrap and Font and Add to list
|
||||
str = str.replace('\r\n', ' ')
|
||||
str = str.replace(f'\\f[{fontSize}]', '')
|
||||
list300.append(str)
|
||||
|
||||
# Pass 2
|
||||
else:
|
||||
if all(x not in str for x in ['_', '@', '>', '/',]) and str != '\r\n':
|
||||
# Decide Wrap
|
||||
if codeList[i]['stringArgs'][0] == '[移]サウンドノベル':
|
||||
width = 40
|
||||
else:
|
||||
width = WIDTH
|
||||
|
||||
# Add Textwrap and Font
|
||||
list300[0] = textwrap.fill(list300[0], width)
|
||||
list300[0] = list300[0].replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
list300[0] = f'\\f[{fontSize}]{list300[0]}\r\n'
|
||||
translatedText += list300[0]
|
||||
list300.pop(0)
|
||||
# Pass 2
|
||||
else:
|
||||
translatedText += str
|
||||
if all(x not in str for x in ['_', '@', '>', '/',]) and str != '\r\n':
|
||||
# Decide Wrap
|
||||
if codeList[i]['stringArgs'][0] == '[移]サウンドノベル':
|
||||
width = 40
|
||||
else:
|
||||
width = WIDTH
|
||||
|
||||
# Write to File
|
||||
if setData:
|
||||
# Formatting Fixes
|
||||
translatedText = translatedText.replace('*"', '* "')
|
||||
translatedText = translatedText.replace('\r\n\r\n', '\r\n')
|
||||
translatedText = re.sub(r'[^\S\r\n]+', ' ', translatedText)
|
||||
if len(codeList[i]['stringArgs']) > 1:
|
||||
codeList[i]['stringArgs'][1] = translatedText
|
||||
else:
|
||||
codeList[i]['stringArgs'][0] = translatedText
|
||||
# Add Textwrap and Font
|
||||
list300[0] = textwrap.fill(list300[0], width)
|
||||
list300[0] = list300[0].replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
list300[0] = f'\\f[{fontSize}]{list300[0]}\r\n'
|
||||
translatedText += list300[0]
|
||||
list300.pop(0)
|
||||
else:
|
||||
translatedText += str
|
||||
|
||||
# Write to File
|
||||
if setData:
|
||||
# Formatting Fixes
|
||||
translatedText = translatedText.replace('*"', '* "')
|
||||
translatedText = translatedText.replace('\r\n\r\n', '\r\n')
|
||||
translatedText = re.sub(r'[^\S\r\n]+', ' ', translatedText)
|
||||
if len(codeList[i]['stringArgs']) > 1:
|
||||
codeList[i]['stringArgs'][1] = translatedText
|
||||
else:
|
||||
codeList[i]['stringArgs'][0] = translatedText
|
||||
|
||||
### Event Code: 250 Common Events
|
||||
if codeList[i]['code'] == 250 and CODE250 == True:
|
||||
|
|
@ -820,7 +823,7 @@ def searchDB(events, pbar, jobList, filename):
|
|||
scenarioList[2].pop(0)
|
||||
|
||||
# Grab Items
|
||||
if table['name'] == 'm_モンスター用名前候補' and ITEMFLAG == True:
|
||||
if table['name'] == 'オーブ' and ITEMFLAG == True:
|
||||
with open('translations.txt', 'a', encoding='utf-8') as file:
|
||||
for item in table['data']:
|
||||
dataList = item['data']
|
||||
|
|
@ -828,7 +831,7 @@ def searchDB(events, pbar, jobList, filename):
|
|||
# Parse #
|
||||
for j in range(len(dataList)):
|
||||
# Name
|
||||
if dataList[j].get('name') == '名前':
|
||||
if dataList[j].get('name') == 'オーブの名前':
|
||||
# Pass 1 (Grab Data)
|
||||
if setData == False:
|
||||
if dataList[j].get('value') != '':
|
||||
|
|
@ -842,9 +845,9 @@ def searchDB(events, pbar, jobList, filename):
|
|||
itemList[0].pop(0)
|
||||
|
||||
# Description 1 (You are my specialz)
|
||||
if dataList[j].get('name') == 'NULL':
|
||||
if dataList[j].get('name') == 'オーブの説明':
|
||||
# Clean String
|
||||
fontSize = 24
|
||||
fontSize = 18
|
||||
translatedText = ''
|
||||
cleanedList = formatDramon(dataList[j].get('value'))
|
||||
for str in cleanedList:
|
||||
|
|
@ -864,9 +867,9 @@ def searchDB(events, pbar, jobList, filename):
|
|||
|
||||
# Add Textwrap and Font
|
||||
tempText = itemList[1][0]
|
||||
# tempText = textwrap.fill(tempText, width)
|
||||
# tempText = tempText.replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
# tempText = f'\\f[{fontSize}]{tempText}\r\n'
|
||||
tempText = textwrap.fill(tempText, width)
|
||||
tempText = tempText.replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
tempText = f'\\f[{fontSize}]{tempText}\r\n'
|
||||
translatedText += tempText
|
||||
itemList[1].pop(0)
|
||||
else:
|
||||
|
|
@ -903,9 +906,9 @@ def searchDB(events, pbar, jobList, filename):
|
|||
|
||||
# Add Textwrap and Font
|
||||
tempText = itemList[2][0]
|
||||
# tempText = textwrap.fill(tempText, width)
|
||||
# tempText = tempText.replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
# tempText = f'\\f[{fontSize}]{tempText}\r\n'
|
||||
tempText = textwrap.fill(tempText, width)
|
||||
tempText = tempText.replace('\n', f'\r\n\\f[{fontSize}]')
|
||||
tempText = f'\\f[{fontSize}]{tempText}\r\n'
|
||||
translatedText += tempText
|
||||
itemList[2].pop(0)
|
||||
else:
|
||||
|
|
|
|||
55
vocab.txt
55
vocab.txt
|
|
@ -1,5 +1,54 @@
|
|||
Here are some vocabulary and terms so that you know the proper spelling and translation.
|
||||
```
|
||||
# Game Characters
|
||||
朱雀 (Suzuku) - Female
|
||||
紅 (Akane) - Female
|
||||
エージェントシリウス (Agent Sirius) - Female
|
||||
白虎 (Shiratora) - Female
|
||||
撫子 (Nadeshiko) - Female
|
||||
鬼塚 (Onizuka) - Female
|
||||
百合花 (Yurika) - Female
|
||||
犬飼 (Inukai) - Female
|
||||
アンナ (Anna) - Female
|
||||
メープル (Maple) - Female
|
||||
鈴懸 (Suzukake) - Female
|
||||
いるか (Iruka) - Female
|
||||
安知理 (Ajiri) - Female
|
||||
結 (Yuu) - Female
|
||||
綾香 (Ayaka) - Female
|
||||
鳥居 (Tori) - Female
|
||||
鈴蘭 (Suzuran) - Female
|
||||
宇宙 (Sora) - Female
|
||||
青龍 (Aotatsu) - Female
|
||||
桔梗 (Kikyo) - Female
|
||||
獅堂 (Shidou) - Female
|
||||
向日葵 (Himawari) - Female
|
||||
大紫 (Omurasaki) - Female
|
||||
菫 (Sumire) - Female
|
||||
亀神 (Kamegami) - Female
|
||||
柊 (Shu) - Female
|
||||
鷲尾 (Washio) - Male
|
||||
菊一 (Kikuichi) - Male
|
||||
花村 (Hanamura) - Female
|
||||
咲夜 (Sakuya) - Female
|
||||
曽我井 (Sogai) - Male
|
||||
四海 (Shikai) - Male
|
||||
志倉 綿 (Shikura Wata) - Female
|
||||
皐月 亜麻 (Satsuki Ama) - Female
|
||||
安知理 結 (Ajiri Yuu) - Female
|
||||
栗山 五十木 (Kuriyama Itsuki) - Male
|
||||
天狐 橙 (Tenkou Daidai) - Female
|
||||
天狐 蜜柑 (Tenkou Mikan) - Female
|
||||
鷹本 椿 (Takamoto Tsubaki) - Female
|
||||
青龍 桃葉 (Aotatsu Momoba) - Female
|
||||
雨宮 星羅 (Amamiya Seira) - Female
|
||||
山内 紫苑 (Yamauchi Shion) - Female
|
||||
野沢 林檎 (Nozawa Ringo) - Female
|
||||
城ヶ峰 柳 (Jougamine Yanagi) - Female
|
||||
山角 翠 (Yamakaku Midori) - Female
|
||||
来杉馬来 (Kisugiba Rai) - Male
|
||||
狼煙蓮 (Noroshi Ren) - Male
|
||||
|
||||
# Lewd Terms
|
||||
マンコ (pussy)
|
||||
おまんこ (vagina)
|
||||
|
|
@ -67,4 +116,10 @@ ME 音量 (ME Volume)
|
|||
堕天使 (Fallen Angel)
|
||||
鬼 (Oni)
|
||||
ローバー (Roper)
|
||||
|
||||
# Skills
|
||||
Highest Repeat Rate No.1 Girl
|
||||
Veteran Courtesan
|
||||
Vital Point Thrust
|
||||
Majikite
|
||||
```
|
||||
Loading…
Reference in a new issue