Skip to content

Commit 001fcd4

Browse files
authored
Merge pull request #55 from neph1/update-v0.20.1
Update v0.20.1
2 parents 3b36036 + e6fddab commit 001fcd4

File tree

11 files changed

+125
-11
lines changed

11 files changed

+125
-11
lines changed

tale/cmds/normal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import itertools
1010
import random
1111
from typing import Iterable, List, Dict, Generator, Union, Optional
12+
from tale.llm.LivingNpc import LivingNpc
1213

1314
from tale.llm.llm_ext import DynamicStory
1415

@@ -1733,7 +1734,7 @@ def do_save(player: Player, parsed: base.ParseResult, ctx: util.Context) -> None
17331734
"""Save the current story to file."""
17341735
story = ctx.driver.story
17351736
if isinstance(story, DynamicStory):
1736-
story.save()
1737+
story.save(save_name=parsed.unparsed)
17371738
else:
17381739
raise ActionRefused("Not a dynamic story")
17391740

tale/llm/LivingNpc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ def notify_action(self, parsed: ParseResult, actor: Living) -> None:
7272
if self.handle_item_result(result, actor) and self.quest:
7373
self.quest.check_completion({"item":result.item, "npc":result.to})
7474
self.do_say(parsed.unparsed, actor)
75+
elif parsed.verb == 'attack' and targeted:
76+
# TODO: should llm decide sentiment?
77+
self.sentiments[actor.title] = 'hostile'
7578
if self.quest and self.quest.is_completed():
7679
# do last to give npc chance to react
7780
self._clear_quest()

tale/llm/llm_ext.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import os
33
import random
4+
import shutil
45
from tale import parse_utils
56
from tale.base import Item, Living, Location
67
from tale.coord import Coord
@@ -125,6 +126,14 @@ def save(self, save_name: str = '') -> None:
125126
with open(os.path.join(save_path, 'llm_cache.json'), "w") as fp:
126127
json.dump(llm_cache.json_dump(), fp, indent=4)
127128

129+
if save_name:
130+
resource_path = os.path.join(save_path, 'resources')
131+
if not os.path.exists(resource_path):
132+
os.mkdir(resource_path)
133+
shutil.copy(os.path.join(os.getcwd(), 'story.py'), os.path.join(save_path, 'story.py'))
134+
if os.path.exists(os.path.join(os.getcwd(), 'resources')):
135+
shutil.copytree(os.path.join(os.getcwd(), 'resources'), resource_path, dirs_exist_ok=True)
136+
128137

129138
def generate_quest(self, npc: LivingNpc, type: QuestType = QuestType.GIVE) -> Quest:
130139
""" Generate a quest for the npc. """

tale/tio/if_browser_io.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,12 @@ def wsgi_handle_story(self, environ: Dict[str, Any], parameters: Dict[str, str],
320320
return self.wsgi_not_modified(start_response)
321321
headers.append(("ETag", etag))
322322
start_response('200 OK', headers)
323+
323324
txt = resource.text.format(story_version=self.driver.story.config.version,
324325
story_name=self.driver.story.config.name,
325326
story_author=self.driver.story.config.author,
326327
story_author_email=self.driver.story.config.author_address)
328+
txt = self.modify_web_page(environ["wsgi.session"]["player_connection"], txt)
327329
return [txt.encode("utf-8")]
328330

329331
def wsgi_handle_eventsource(self, environ: Dict[str, Any], parameters: Dict[str, str],
@@ -465,6 +467,13 @@ def wsgi_serve_static(self, path: str, environ: Dict[str, Any], start_response:
465467
data = resource.data
466468
start_response('200 OK', headers)
467469
return [data]
470+
471+
def modify_web_page(self, player_connection: PlayerConnection, html_content: str) -> None:
472+
"""Modify the html before it is sent to the browser."""
473+
if not "wizard" in player_connection.player.privileges:
474+
html_content = html_content.replace('<input type="file" id="loadCharacterInput" accept=".json, .png, .jpeg, .jpg">', '')
475+
return html_content
476+
468477

469478

470479
class TaleWsgiApp(TaleWsgiAppBase):

tale/tio/mud_browser_io.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ def wsgi_handle_about(self, environ: Dict[str, Any], parameters: Dict[str, str],
141141
num_players=len(self.driver.all_players),
142142
player_table=player_table_txt)
143143
return [txt.encode("utf-8")]
144+
145+
def modify_web_page(self, player_connection: PlayerConnection, html_content: str) -> None:
146+
html_content = super().modify_web_page(player_connection, html_content)
147+
html_content = html_content.replace('<input type="button" id="saveButton" value="Save story" onclick="showSaveDialog()" readonly/>', '')
148+
return html_content
144149

145150

146151
class CustomRequestHandler(WSGIRequestHandler):

tale/web/save_story.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
function showSaveDialog() {
3+
document.getElementById('dialogOverlay').style.display = 'flex';
4+
}
5+
6+
function closeSaveDialog() {
7+
document.getElementById('dialogOverlay').style.display = 'none';
8+
}
9+
10+
function saveStory() {
11+
const filenameInput = document.getElementById('filenameInput');
12+
const filename = filenameInput.value.trim().replace(/ /g, '_');
13+
14+
// Close the dialog
15+
closeSaveDialog();
16+
var ajax = new XMLHttpRequest();
17+
ajax.open("POST", "input", true);
18+
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8");
19+
var encoded_cmd = encodeURIComponent('save_story ' + filename);
20+
console.log("Saving story: " + encoded_cmd);
21+
ajax.send("cmd=" + encoded_cmd);
22+
}

tale/web/script.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,6 @@ function process_text(json)
7676
populateNpcDropdown(json["npcs"]);
7777
}
7878
setLocationImage(json["location"].toLowerCase().replace(/ /g, '_') + '.jpg');
79-
// if (json.hasOwnProperty("location_image") && json["location_image"] !== '') {
80-
// setLocationImage(json["location_image"]);
81-
// } else {
82-
// setLocationImage(null);
83-
// }
8479
txtdiv.innerHTML += json["text"];
8580
if(!document.smoothscrolling_busy) smoothscroll(txtdiv, 0);
8681
}

tale/web/story.html

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88
<script type="application/javascript" src="static/eventsource.min.js"></script>
99
<script type="application/javascript" src="static/script.js"></script>
1010
<script type="application/javascript" src="static/handle_upload.js"></script>
11+
<script type="application/javascript" src="static/save_story.js"></script>
1112
</head>
1213
<body onload="setup()">
14+
<div id="dialogOverlay">
15+
<div id="saveDialog">
16+
<label for="filenameInput">Enter new name (or leave blank to overwrite current):</label>
17+
<input type="text" id="filenameInput">
18+
<button onclick="saveStory()">Save</button>
19+
<button onclick="closeSaveDialog()">Cancel</button>
20+
</div>
21+
</div>
1322
<div id="tale-page">
1423
<div id="top-container">
1524
<h1 title="version: {story_version} -- author: {story_author} -- {story_author_email}"><img src="static/logo.png" id="img-logo"> {story_name}</h1>
@@ -35,14 +44,15 @@ <h2>Your browser doesn't have Javascript or it is disabled. You can't use this w
3544
<input type="submit" value="&#9166;" onclick="submit_cmd(); return false;" readonly/>
3645
<input type="submit" value="&RightArrowBar;" name="autocomplete" id="button-autocomplete" onclick="autocomplete_cmd(); return false;" readonly/>
3746
<input type="submit" value="&#128682" name="quit" id="button-quit" onclick="quit_clicked(); return false;" readonly/>
47+
3848
</form>
39-
<input type="file" id="fileInput" accept=".json, .png, .jpeg, .jpg">
40-
49+
<input type="file" id="loadCharacterInput" accept=".json, .png, .jpeg, .jpg">
50+
<input type="button" id="saveButton" value="Save story" onclick="showSaveDialog()" readonly/>
4151
</div>
4252
</div>
4353
</body>
4454
<script>
45-
document.getElementById('fileInput').addEventListener('change', handleFile);
55+
document.getElementById('loadCharacterInput').addEventListener('change', handleFile);
4656
</script>
4757

4858
</html>

tale/web/style.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,22 @@ p, pre {
187187

188188
#top-container {
189189
display: flex;
190+
}
191+
192+
#dialogOverlay {
193+
display: none;
194+
position: fixed;
195+
top: 0;
196+
left: 0;
197+
width: 100%;
198+
height: 100%;
199+
background: rgba(0, 0, 0, 0.5);
200+
justify-content: center;
201+
align-items: center;
202+
}
203+
204+
#saveDialog {
205+
background: #fff;
206+
padding: 20px;
207+
border-radius: 8px;
190208
}

tests/test_browser.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11

22

3+
from os import getcwd
34
from wsgiref.simple_server import WSGIServer
45
from tale.player import PlayerConnection
5-
from tale.tio.if_browser_io import HttpIo
6+
from tale.tio.if_browser_io import HttpIo, TaleWsgiApp
7+
from tale.tio.mud_browser_io import TaleMudWsgiApp
68
from tale.web.web_utils import create_chat_container
9+
from tests.supportstuff import FakeDriver
710

811

912
class TestHttpIo:
@@ -41,4 +44,32 @@ def test_create_chat_container(self):
4144

4245
assert "chat-container" in result
4346
assert '<div class="user-name" content="Bloated Murklin"></div>' in result
44-
assert '<div class="text-field" type="text">Hello World!</div>' in result
47+
assert '<div class="text-field" type="text">Hello World!</div>' in result
48+
49+
def test_remove_load_character_button(self):
50+
connection = PlayerConnection()
51+
driver = FakeDriver()
52+
wsgi_app = TaleWsgiApp(driver=driver, player_connection=connection, use_ssl=False, ssl_certs=None)
53+
54+
load_button = '<input type="file" id="loadCharacterInput" accept=".json, .png, .jpeg, .jpg">'
55+
with open('tale/web/story.html', 'r') as file:
56+
contents = file.read()
57+
assert load_button in contents
58+
result = wsgi_app.modify_web_page(connection, contents)
59+
60+
assert load_button not in result
61+
62+
def test_remove_save_button(self):
63+
connection = PlayerConnection()
64+
driver = FakeDriver()
65+
wsgi_app = TaleMudWsgiApp(driver=driver, use_ssl=False, ssl_certs=None)
66+
67+
save_button = '<input type="button" id="saveButton" value="Save story" onclick="showSaveDialog()" readonly/>'
68+
with open('tale/web/story.html', 'r') as file:
69+
contents = file.read()
70+
assert save_button in contents
71+
result = wsgi_app.modify_web_page(connection, contents)
72+
73+
assert save_button not in result
74+
75+

0 commit comments

Comments
 (0)