Skip to content

Commit c37b04b

Browse files
authored
Merge pull request #53 from neph1/v0.19.0
V0.19.0
2 parents 88c4901 + d2cb9be commit c37b04b

31 files changed

+661
-276
lines changed

backend_kobold_cpp.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ ENDPOINT: "/api/v1/generate"
33
STREAM: False
44
STREAM_ENDPOINT: "/api/extra/generate/stream"
55
DATA_ENDPOINT: "/api/extra/generate/check"
6-
DEFAULT_BODY: '{"stop_sequence": "\n\n\n", "max_length":750, "max_context_length":4096, "temperature":0.5, "top_k":120, "top_a":0.0, "top_p":0.85, "typical_p":1.0, "tfs":1.0, "rep_pen":1.2, "rep_pen_range":256, "sampler_order":[6,0,1,3,4,2,5], "seed":-1}'
6+
DEFAULT_BODY: '{"stop_sequence": "\n\n\n\n", "max_length":750, "max_context_length":4096, "temperature":0.5, "top_k":120, "top_a":0.0, "top_p":0.85, "typical_p":1.0, "tfs":1.0, "rep_pen":1.2, "rep_pen_range":256, "sampler_order":[6,0,1,3,4,2,5], "seed":-1}'
77
ANALYSIS_BODY: '{}'
88
GENERATION_BODY: '{"stop_sequence": "\n\n\n\n", "max_length":750, "max_context_length":4096, "temperature":1.0, "top_k":120, "top_a":0.0, "top_p":0.85, "typical_p":1.0, "tfs":1.0, "rep_pen":1.2, "rep_pen_range":256, "sampler_order":[6,0,1,3,4,2,5], "seed":-1}'

backend_llama_cpp.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
URL: "http://127.0.0.1:8081"
1+
URL: "http://127.0.0.1:8080"
22
ENDPOINT: "/v1/chat/completions"
33
STREAM: False
44
OPENAI_HEADERS: '{"Content-Type":"application/json", "Authorization":""}'

llm_config.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ WORD_LIMIT: 200
22
BACKEND: "kobold_cpp" # valid options: "openai", "llama_cpp", "kobold_cpp"
33
ANALYSIS_BODY: '{}'
44
MEMORY_SIZE: 512
5-
PRE_PROMPT: 'Below is an instruction that describes a task, paired with an input that provides further context. As an game keeper for an RPG, write a response that appropriately completes the request.'
5+
PRE_PROMPT: 'You are a creative game keeper. You craft detailed worlds and interesting characters with unique and deep personalities for the player to interact with.'
66
BASE_PROMPT: "[Story context: {story_context}]; [History: {history}]; [USER_START] Rewrite [{input_text}] in your own words using the supplied Context and History to create a background for your text. Use about {max_words} words."
77
ACTION_PROMPT: "[Story context: {story_context}]; [History: {history}]; [USER_START] Rewrite the Action, and nothing else, in your own words using the supplied Context and Location. History is what happened before. Use less than {max_words} words. [Action: {input_text}]."
88
DIALOGUE_PROMPT: 'Story context: {story_context}; Location: {location}; The following is a conversation between {character1} and {character2}; {character1}:{character1_description}; {character2}:{character2_description}; {character2}s sentiment towards {character1}: {sentiment}. Write a single response as {character2} in third person pov, using {character2} description. If {character2} has a quest active, they will discuss it based on its status. Respond in JSON using this template: {{"response":"may be both dialogue and action.", "sentiment":"sentiment based on response", "give":"if any physical item of {character2}s is given as part of the dialogue. Or nothing."}}. [USER_START]Continue the following conversation as {character2}: {previous_conversation}'
@@ -28,5 +28,6 @@ PLAYER_ENTER_PROMPT: 'Story context: {story_context}; World info: {world_info};
2828
QUEST_PROMPT: '[Story context: {story_context}]; World info: {world_info}; Zone info: {zone_info}; Character: {character_card}; [USER_START] In an RPG described as {story_type}, {character_name} needs someone to perform a task. Based on the following input, come up with a suitable reason for it, using {character_name}s personality and history. Task info: {base_quest}. Fill in this JSON template and do not write anything else: {{"reason":""}} \n\n '
2929
NOTE_QUEST_PROMPT: '[Story context: {story_context}]; World info: {world_info}; Zone info: {zone_info}; [USER_START]For an RPG described as {story_type}, generate a quest that starts from reading a note. The reader must find and talk to a person. Fill in the following JSON template and write nothing else.: {{"reason": "only what the note says. 50 words.", "type":"talk", "target":"who to talk to", "location":"", "name":"name of quest"}}'
3030
NOTE_LORE_PROMPT: '[Story context: {story_context}]; World info: {world_info}; Zone info: {zone_info}; [USER_START]For an RPG described as {story_type}, Decide what is written on a note that has been found. Use the provided story and world information to generate a piece of lore. Use about 50 words.'
31-
USER_START: '[INST]'
32-
USER_END: '[/INST]'
31+
ACTION_PROMPT: 'Story context: {story_context};<location>{location}</location> \nAct as as {character_name} in a {story_type} RPG and perform an action.<description>{character}</description> <actions>{actions}</actions>. <items>{location_items}</items>. <characters>{characters}</characters> <exits>{exits}</exits>.\nPick an action according to {character_name}s description and mood. If suitable, select something to perform the action on (target). Make sure the action is from the list and is related to {character_name}s thoughts.\n Respond using JSON in the following format: """{{"goal": reason for action, "thoughts":thoughts about performing action, "action":action chosen, "target":character, item or exit or description, "text": if anything is said during the action}}"""<history>{history}</history>'
32+
USER_START: '### Instruction:'
33+
USER_END: '### Response:'

stories/prancingllama/npcs/npcs.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,6 @@ class Shanda(Patron):
101101
def init(self) -> None:
102102
self.stats.set_weapon_skill(weapon_type=WeaponType.UNARMED, value=65)
103103

104-
def allow_give_item(self, item: Item, actor: Optional[Living]) -> None:
105-
if item.name == "rat_skull":
106-
self.sentiments(actor.title, 'impressed')
107-
self.do_say(f'{actor.title} gives Shanda a giant rat skull', actor=actor)
108104

109105
class Rat(Living):
110106
def init(self) -> None:
@@ -131,7 +127,7 @@ def do_idle_action(self, ctx: Context) -> None:
131127

132128
shanda = Shanda("Shanda Heard", "f", age=31, descr="A fierce looking woman, with a face as if chiseled from granite and a red bandana around her wild hair. She keeps an unblinking gaze on her surroundings.", personality="She's a heavy drinker and boaster, and for a drink she will spill tales of her conquests and battles and long lost love. She's feared by many but respected by all for her prowess in battle.", short_descr="A fierce looking woman sitting by a table, whose eyes seem to follow you.")
133129
shanda.aliases = {"shanda", "fierce woman", "woman by table"}
134-
shanda.quest = Quest(name="bring a rat skull", reason="to prove your worth", giver=shanda.name, type=QuestType.GIVE, target="rat_skull")
130+
shanda.quest = Quest(name="bring a rat skull", reason="prove your worth to Shanda", giver=shanda.name, type=QuestType.GIVE, target="rat_skull")
135131

136132
count_karta = Patron("Count of Karta", "m", age=43, descr="A hood shadows his facial features, but a prominent jaw juts out from beneath it. His mouth seems to be working constantly, as if muttering about something.", personality="Having fled from an attempt on his life, and being discredited, he has come here to plan his revenge. He seems to have gold and is looking for able bodies to help him.", short_descr="A mysterious man by the fire.")
137133
count_karta.aliases = {"count", "karta", "mysterious man", "hooded man"}

stories/prancingllama/story.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Story(DynamicStory):
3131
config.startlocation_player = "prancingllama.entrance"
3232
config.startlocation_wizard = "prancingllama.entrance"
3333
config.zones = ["prancingllama"]
34-
config.context = "The final outpost high up in a cold, craggy mountain range. It's frequented by adventurers and those looking to avoid attention."
34+
config.context = "The final outpost high up in a cold, craggy mountain range. It's frequented by adventurers and those seeking to avoid attention."
3535
config.type = "A low level fantasy adventure with focus of character building and interaction."
3636
config.custom_resources = True
3737

tale/base.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ def __init__(self, name: str, title: str = "", *, descr: str = "", short_descr:
285285
self.verbs = {} # type: Dict[str, str]
286286
# register all periodical tagged methods
287287
self.story_data = {} # type: Dict[Any, Any] # not used by Tale itself, story can put custom data here. Use builtin types only.
288+
self.visible = True # can this object be seen by others?
288289
self.init()
289290
if util.get_periodicals(self):
290291
if mud_context.driver is None:
@@ -766,7 +767,7 @@ def look(self, exclude_living: 'Living'=None, short: bool=False) -> Sequence[str
766767
item_names = sorted(item.name for item in self.items)
767768
paragraphs.append("You see: " + lang.join(item_names))
768769
if self.livings:
769-
living_names = sorted(living.name for living in self.livings if living != exclude_living)
770+
living_names = sorted(living.name for living in self.livings if living != exclude_living and living.visible)
770771
if living_names:
771772
paragraphs.append("Present here: " + lang.join(living_names))
772773
return paragraphs
@@ -783,8 +784,8 @@ def look(self, exclude_living: 'Living'=None, short: bool=False) -> Sequence[str
783784
exit_paragraph.append(exit.short_description)
784785
paragraphs.append(" ".join(exit_paragraph))
785786
items_and_livings = [] # type: List[str]
786-
items_with_short_descr = [item for item in self.items if item.short_description]
787-
items_without_short_descr = [item for item in self.items if not item.short_description]
787+
items_with_short_descr = [item for item in self.items if item.short_description and item.visible]
788+
items_without_short_descr = [item for item in self.items if not item.short_description and item.visible]
788789
uniq_descriptions = set()
789790
if items_with_short_descr:
790791
for item in items_with_short_descr:
@@ -793,8 +794,8 @@ def look(self, exclude_living: 'Living'=None, short: bool=False) -> Sequence[str
793794
if items_without_short_descr:
794795
titles = sorted([lang.a(item.title) for item in items_without_short_descr])
795796
items_and_livings.append("You see " + lang.join(titles) + ".")
796-
livings_with_short_descr = [living for living in self.livings if living != exclude_living and living.short_description]
797-
livings_without_short_descr = [living for living in self.livings if living != exclude_living and not living.short_description]
797+
livings_with_short_descr = [living for living in self.livings if living != exclude_living and living.short_description and living.visible]
798+
livings_without_short_descr = [living for living in self.livings if living != exclude_living and not living.short_description and living.visible]
798799
if livings_without_short_descr:
799800
titles = sorted(living.title for living in livings_without_short_descr)
800801
if titles:
@@ -1406,7 +1407,7 @@ def locate_item(self, name: str, include_inventory: bool=True, include_location:
14061407
break
14071408
return (found, containing_object) if found else (None, None)
14081409

1409-
def start_attack(self, victim: 'Living') -> None:
1410+
def start_attack(self, victim: 'Living') -> combat.Combat:
14101411
"""Starts attacking the given living for one round."""
14111412
attacker_name = lang.capital(self.title)
14121413
victim_name = lang.capital(victim.title)
@@ -1436,6 +1437,7 @@ def start_attack(self, victim: 'Living') -> None:
14361437
combat.produce_remains(util.Context, self)
14371438
if victim.stats.hp < 1:
14381439
combat.produce_remains(util.Context, victim)
1440+
return c
14391441

14401442
def allow_give_money(self, amount: float, actor: Optional['Living']) -> None:
14411443
"""Do we accept money? Raise ActionRefused if not."""
@@ -1518,7 +1520,7 @@ def _move_follow(self, target_location: Location, ctx: util.Context) -> None:
15181520

15191521
def check_stat(self, stat : str):
15201522
if stat == 'hp':
1521-
return self.stat.hp
1523+
return self.stats.hp
15221524

15231525
@property
15241526
def wielding(self) -> Weapon:
@@ -1528,16 +1530,30 @@ def wielding(self) -> Weapon:
15281530
@wielding.setter
15291531
def wielding(self, weapon: Optional[Weapon]) -> None:
15301532
"""Wield a weapon. If weapon is None, unwield."""
1533+
if not weapon and not self.__wielding:
1534+
raise ActionRefused("You're not wielding anything.")
15311535
self.__wielding = weapon
15321536
self.stats.wc = weapon.wc if self.__wielding else 0
1537+
if weapon:
1538+
self.tell_others("{Actor} wields %s." % self.wielding.title, evoke=True, short_len=True)
1539+
self.tell("You wield %s." % self.wielding.title)
1540+
elif self.__wielding:
1541+
self.tell_others("{Actor} unwields %s." % self.__wielding.title, evoke=True, short_len=True)
1542+
self.tell("You unwield %s." % self.__wielding.title)
15331543

15341544
def set_wearable(self, wearable: Optional[Wearable], wear_location: Optional[wearable.WearLocation]) -> None:
15351545
""" Wear an item if item is not None, else unwear location"""
15361546
if wearable:
15371547
loc = wear_location if wear_location else wearable.wear_location
15381548
self.__wearing[loc] = wearable
1549+
self.tell_others("{Actor} puts on %s." % wearable.title, evoke=True, short_len=True)
1550+
self.tell("You put on %s." % wearable.title)
15391551
elif wear_location:
1540-
self.__wearing.pop(wear_location, None)
1552+
item = self.__wearing.pop(wear_location, None)
1553+
if item:
1554+
self.tell_others("{Actor} removes %s." % item.title, evoke=True, short_len=True)
1555+
self.tell("You remove %s." % item.title)
1556+
15411557

15421558
def get_wearable(self, location: wearable.WearLocation) -> Optional[Wearable]:
15431559
"""Return the wearable item at the given location, or None if no item is worn there."""

tale/cmds/normal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@ def do_save(player: Player, parsed: base.ParseResult, ctx: util.Context) -> None
17391739

17401740
@cmd("eat", "drink")
17411741
def do_consume(player: Player, parsed: base.ParseResult, ctx: util.Context) -> None:
1742-
"""Eat or drinksomething."""
1742+
"""Eat or drink something."""
17431743
if len(parsed.args) != 1:
17441744
raise ParseError("You need to specify the item to consume")
17451745
try:

tale/cmds/wizard.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,4 +798,22 @@ def do_load_character(player: Player, parsed: base.ParseResult, ctx: util.Contex
798798
ctx.driver.load_character(player, path)
799799
except FileNotFoundError:
800800
raise ActionRefused("File not found")
801+
802+
@wizcmd("set_visibility")
803+
def do_set_visible(player: Player, parsed: base.ParseResult, ctx: util.Context) -> None:
804+
"""Set the visibility of a creature."""
805+
if len(parsed.args) != 2:
806+
raise ParseError("You need to specify the object and the visibility(true or false)")
807+
try:
808+
object = player.location.search_living(parsed.args[0])
809+
if not object:
810+
object = player.search_item(parsed.args[0], include_inventory=True, include_location=True)
811+
if not object:
812+
raise ParseError("No object found")
813+
814+
visible = parsed.args[1].lower() == 'true' or parsed.args[1] == '1'
815+
object.visible = visible
816+
player.tell("%s visibility set to %s" % (object, visible))
817+
except ValueError as x:
818+
raise ActionRefused(str(x))
801819

tale/combat.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ def resolve_attack(self) -> (str, int, int):
5454

5555
text_result, damage_to_attacker = self._round(self.defender, self.attacker)
5656
texts.extend(text_result)
57+
58+
if self.defender.stats.hp - damage_to_defender < 0:
59+
texts.append(f'{self.defender.title} dies')
60+
if self.attacker.stats.hp - damage_to_attacker < 0:
61+
texts.append(f'{self.attacker.title} dies')
5762

5863
return ', '.join(texts), damage_to_attacker, damage_to_defender
5964

@@ -78,8 +83,6 @@ def _round(self, actor1: 'base.Living', actor2: 'base.Living') -> ([str], int):
7883
texts.append(f', {actor2.title} is injured')
7984
else:
8085
texts.append(f', {actor2.title} is unharmed')
81-
if actor2.stats.hp - damage_to_defender < 1:
82-
texts.append(f', {actor2.title} dies')
8386
return texts, damage_to_defender
8487
elif attack_result > 50:
8588
texts.append(f'{actor1.title} misses completely')

tale/driver.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ def load_character(self, player: player.Player, path: str):
843843
if not char_data:
844844
raise errors.TaleError("Character not found.")
845845
character = CharacterV2().from_json(char_data)
846-
npc = LivingNpc(name = character.name.lower(),
846+
npc = StationaryNpc(name = character.name.lower(),
847847
gender = character.gender,
848848
title = character.name,
849849
descr = character.appearance,
@@ -852,8 +852,19 @@ def load_character(self, player: player.Player, path: str):
852852
personality = character.personality,
853853
race = character.race,
854854
occupation = character.occupation)
855+
npc.autonomous = character.autonomous
856+
wearing = character.wearing.split(',')
857+
for item in wearing:
858+
if item:
859+
wearable = base.Wearable(name=item.lower().trim())
860+
npc.set_wearable(wearable)
861+
855862
npc.following = player
856863
npc.stats.hp = character.hp
864+
if isinstance(self.story, DynamicStory):
865+
dynamic_story = typing.cast(DynamicStory, self.story)
866+
dynamic_story.world.add_npc(npc)
867+
857868
player.location.insert(npc, None)
858869
player.location.tell("%s arrives." % npc.title)
859870

0 commit comments

Comments
 (0)