├── lampmud ├── env │ ├── __init__.py │ ├── instancemgr.py │ ├── movement.py │ └── instance.py ├── comm │ ├── __init__.py │ └── broadcast.py ├── editor │ ├── __init__.py │ ├── imports.py │ ├── shared.py │ ├── scripts.py │ └── start.py ├── lpmud │ ├── combat │ │ ├── __init__.py │ │ ├── system.py │ │ └── fight.py │ ├── feature │ │ ├── __init__.py │ │ └── touchstone.py │ ├── __init__.py │ ├── appstart.py │ ├── server.py │ ├── archetype.py │ ├── newsetup.py │ ├── article.py │ ├── attributes.py │ ├── env.py │ ├── mobile.py │ └── player.py ├── setup │ ├── __init__.py │ ├── update.py │ └── engine.py ├── __init__.py ├── server │ ├── __init__.py │ └── messages.py ├── model │ ├── __init__.py │ ├── script.py │ ├── area.py │ ├── race.py │ ├── mobile.py │ ├── item.py │ ├── player.py │ └── article.py └── mud │ ├── __init__.py │ ├── tools.py │ ├── inventory.py │ ├── action.py │ ├── chat.py │ ├── socials.py │ └── target.py ├── webclient ├── lampost.png ├── mud │ ├── css │ │ └── mud.css │ ├── sound │ │ ├── atone.mp3 │ │ ├── beep_ping.mp3 │ │ └── sound_list.html │ ├── image │ │ └── friendNotify.png │ ├── view │ │ ├── messages_tab.html │ │ ├── system_msg.html │ │ ├── channel_tab.html │ │ ├── player_msg.html │ │ ├── data_tabs.html │ │ ├── player_list_tab.html │ │ ├── friend_req_msg.html │ │ ├── status_tab.html │ │ └── main.html │ ├── dialogs │ │ ├── select_character.html │ │ ├── password_reset.html │ │ ├── forgot_password.html │ │ ├── forgot_name.html │ │ ├── new_account.html │ │ └── new_character.html │ └── js │ │ ├── status.js │ │ ├── character.js │ │ ├── storage.js │ │ ├── comm.js │ │ └── data.js ├── editor │ ├── css │ │ └── editor.css │ ├── view │ │ ├── no_item.html │ │ ├── data_error.html │ │ ├── outside_edit.html │ │ ├── social_cheat.html │ │ ├── display.html │ │ ├── input_list.html │ │ ├── imports.html │ │ ├── simple_list.html │ │ ├── option_list.html │ │ ├── edit_list.html │ │ ├── value_set.html │ │ ├── editor_main.html │ │ ├── area.html │ │ ├── admin_view.html │ │ ├── script.html │ │ ├── user.html │ │ ├── mobile.html │ │ ├── defense.html │ │ ├── social.html │ │ ├── race.html │ │ ├── config.html │ │ ├── attack.html │ │ └── player.html │ ├── panels │ │ ├── action_script.html │ │ ├── article_load.html │ │ ├── shadow_script.html │ │ ├── modify_exit.html │ │ ├── feature.html │ │ ├── modify_script.html │ │ ├── touchstone.html │ │ ├── extra.html │ │ ├── new_script.html │ │ ├── room_reset.html │ │ ├── new_exit.html │ │ └── entrance.html │ ├── fragments │ │ ├── owner_id.html │ │ ├── read_access.html │ │ ├── write_access.html │ │ ├── aliases.html │ │ └── script_list.html │ ├── js │ │ ├── display_editor.js │ │ ├── admin.js │ │ ├── editor_filters.js │ │ ├── imports_editor.js │ │ ├── area_editor.js │ │ ├── player_editor.js │ │ ├── socials_editor.js │ │ ├── feature_editors.js │ │ └── skills_editor.js │ └── dialogs │ │ ├── script_selector.html │ │ └── select_db.html ├── common │ ├── dialogs │ │ ├── loading.html │ │ ├── alert.html │ │ └── prompt.html │ └── js │ │ ├── directives.js │ │ └── autofill-event.js └── lampost.html ├── .gitignore ├── .gitattributes ├── lampost_mud.py ├── lampost_tools.py ├── lampost_setup.py ├── LICENSE.txt ├── README.md └── conf └── main.yaml /lampmud/env/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lampmud/comm/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lampmud/editor/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lampmud/lpmud/combat/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lampmud/setup/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lampmud/__init__.py: -------------------------------------------------------------------------------- 1 | from . import server 2 | -------------------------------------------------------------------------------- /lampmud/server/__init__.py: -------------------------------------------------------------------------------- 1 | from . import messages 2 | -------------------------------------------------------------------------------- /lampmud/lpmud/feature/__init__.py: -------------------------------------------------------------------------------- 1 | from . import store, touchstone 2 | -------------------------------------------------------------------------------- /webclient/lampost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genzgd/Lampost-Mud/HEAD/webclient/lampost.png -------------------------------------------------------------------------------- /webclient/mud/css/mud.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | overflow-y: hidden; 4 | } 5 | -------------------------------------------------------------------------------- /lampmud/model/__init__.py: -------------------------------------------------------------------------------- 1 | from . import area, article, entity, item, mobile, player, race, script 2 | -------------------------------------------------------------------------------- /lampmud/mud/__init__.py: -------------------------------------------------------------------------------- 1 | from . import target, immortal, group, socials, inventory, chat, mudcore 2 | -------------------------------------------------------------------------------- /webclient/mud/sound/atone.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genzgd/Lampost-Mud/HEAD/webclient/mud/sound/atone.mp3 -------------------------------------------------------------------------------- /webclient/editor/css/editor.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | 5 | layout-bar { 6 | height: 20px; 7 | } -------------------------------------------------------------------------------- /lampmud/lpmud/__init__.py: -------------------------------------------------------------------------------- 1 | from . import attributes, entity, player, mobile, archetype, feature, env, article, server 2 | -------------------------------------------------------------------------------- /webclient/mud/sound/beep_ping.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genzgd/Lampost-Mud/HEAD/webclient/mud/sound/beep_ping.mp3 -------------------------------------------------------------------------------- /webclient/mud/image/friendNotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genzgd/Lampost-Mud/HEAD/webclient/mud/image/friendNotify.png -------------------------------------------------------------------------------- /webclient/mud/sound/sound_list.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /webclient/editor/view/no_item.html: -------------------------------------------------------------------------------- 1 |
2 | No item currently selected. 3 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.idea 3 | src_ext 4 | data 5 | webclient/local_lib 6 | webclient/fonts 7 | webclient/test.html 8 | *.log 9 | dev 10 | 11 | -------------------------------------------------------------------------------- /webclient/editor/panels/action_script.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
-------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.py text eol=lf 2 | *.js text eol=lf 3 | *.html text eol=lf 4 | *.js text eol=lf 5 | *.css text eol=lf 6 | .gitattributes text eol=lf 7 | .gitignore text eol=lf -------------------------------------------------------------------------------- /webclient/editor/view/data_error.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{errors[type]}} 4 |
5 | -------------------------------------------------------------------------------- /lampmud/model/script.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbo import ChildDBO 2 | from lampost.gameops.script import UserScript 3 | 4 | 5 | class MudScript(UserScript, ChildDBO): 6 | dbo_key_type = 'script' 7 | dbo_parent_type = 'area' 8 | -------------------------------------------------------------------------------- /webclient/editor/view/outside_edit.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Object updated by another user. 4 |
5 | -------------------------------------------------------------------------------- /webclient/mud/view/messages_tab.html: -------------------------------------------------------------------------------- 1 |
2 |
You currently have no messages.
3 |
4 |
5 |
6 | -------------------------------------------------------------------------------- /webclient/common/dialogs/loading.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lampmud/lpmud/appstart.py: -------------------------------------------------------------------------------- 1 | from importlib import import_module 2 | 3 | from lampost.di import resource 4 | 5 | from lampmud.lpmud.combat import system 6 | 7 | 8 | def start_engine(args): 9 | import_module('lampmud.editor.start') 10 | resource.register('action_system', system) 11 | -------------------------------------------------------------------------------- /webclient/editor/fragments/owner_id.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 5 | 6 |
-------------------------------------------------------------------------------- /webclient/editor/fragments/read_access.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 5 | 6 |
-------------------------------------------------------------------------------- /lampmud/model/area.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbo import ParentDBO 2 | from lampost.db.dbofield import DBOField 3 | 4 | 5 | class Area(ParentDBO): 6 | dbo_key_type = "area" 7 | dbo_set_key = "areas" 8 | dbo_children_types = ['room', 'mobile', 'article', 'script'] 9 | 10 | name = DBOField() 11 | desc = DBOField() 12 | next_room_id = DBOField(0) 13 | -------------------------------------------------------------------------------- /webclient/editor/fragments/write_access.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 5 | 6 |
-------------------------------------------------------------------------------- /webclient/mud/view/system_msg.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{msgTime(msg)}} 4 | 5 |
6 |
  {{msg.content}}
7 |
-------------------------------------------------------------------------------- /lampmud/mud/tools.py: -------------------------------------------------------------------------------- 1 | def combat_log(source, message, target=None): 2 | if hasattr(source.env, 'combat_log'): 3 | try: 4 | message = message.combat_log() 5 | except AttributeError: 6 | try: 7 | message = message() 8 | except TypeError: 9 | pass 10 | source.env.broadcast(s=message, source=source, target=target) 11 | -------------------------------------------------------------------------------- /webclient/mud/view/channel_tab.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
No recent messages.
4 |
{{msgTime(msg)}}  
5 |
6 | -------------------------------------------------------------------------------- /lampmud/lpmud/server.py: -------------------------------------------------------------------------------- 1 | from lampost.server.link import link_route 2 | from lampost.di.resource import Injected, module_inject 3 | 4 | db = Injected('datastore') 5 | module_inject(__name__) 6 | 7 | 8 | @link_route('new_char_data') 9 | def _new_char_data(**_): 10 | return {'races': {race.dbo_id: _race_dto(race) for race in db.load_object_set('race')}} 11 | 12 | 13 | def _race_dto(race): 14 | return {'name' : race.name, 'desc': race.desc} 15 | -------------------------------------------------------------------------------- /webclient/mud/view/player_msg.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{msgTime(msg)}} 4 | 5 |
6 |
  {{msg.content}}
7 |
    -- {{msg.source}}
8 |
-------------------------------------------------------------------------------- /webclient/editor/js/display_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('DisplayEditorCtrl', ['$scope', 'lpRemote', 'lpEvent', function ($scope, lpRemote) { 2 | 3 | $scope.updateDisplays = function () { 4 | lpRemote.request('editor/display/update', {displays: $scope.displays}, true); 5 | }; 6 | 7 | lpRemote.request('editor/display/list').then(function (displays) { 8 | $scope.displays = displays; 9 | $scope.ready = true; 10 | }) 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /webclient/editor/view/social_cheat.html: -------------------------------------------------------------------------------- 1 |
2 |
Social/Message Cheat Sheet 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
${{token.id}}{{token.token}}
12 |
13 | -------------------------------------------------------------------------------- /lampost_mud.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from lampost.setup import startargs 4 | from lampost.util import logging 5 | 6 | 7 | if __name__ != "__main__": 8 | print("Invalid usage") 9 | sys.exit(2) 10 | 11 | args = startargs.main_parser.parse_args() 12 | logging.init_config(args) 13 | logging.root_logger.info("Started with args {}", args) 14 | 15 | from lampost.di import resource 16 | resource.register('log', logging.LogFactory()) 17 | 18 | from lampmud.setup import engine 19 | engine.start(args) 20 | -------------------------------------------------------------------------------- /webclient/mud/view/data_tabs.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 |
9 |
10 |
-------------------------------------------------------------------------------- /lampost_tools.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if __name__ != "__main__": 4 | print("Invalid usage") 5 | sys.exit(2) 6 | 7 | from lampost.util import logging 8 | from lampost.setup import startargs 9 | 10 | args = startargs.tools_parser.parse_args() 11 | 12 | logging.init_config(args) 13 | logging.root_logger.info("Started tools with args {}", args) 14 | 15 | # We set the logging configuration before importing other modules so that the root logger is properly configured 16 | from lampost.setup import tools 17 | 18 | getattr(tools, args.op)(args) 19 | -------------------------------------------------------------------------------- /webclient/editor/view/display.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Default Display

4 |
5 | 6 |
7 |
8 | 9 |
10 |
{{display.desc}}
11 |
12 |
13 | 14 |
-------------------------------------------------------------------------------- /webclient/mud/view/player_list_tab.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
NameStatusLocation
{{player.name}}{{player.status}}{{player.loc}}
18 |
19 | 20 | -------------------------------------------------------------------------------- /lampmud/model/race.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbo import KeyDBO, OwnerDBO 2 | from lampost.db.dbofield import DBOField, DBOLField 3 | 4 | base_attr_value = 5 5 | 6 | 7 | class PlayerRace(KeyDBO, OwnerDBO): 8 | dbo_key_type = "race" 9 | dbo_set_key = "races" 10 | 11 | attr_list = [] 12 | 13 | name = DBOField("Unnamed") 14 | desc = DBOField('') 15 | base_attrs = DBOField({}) 16 | start_room = DBOLField(dbo_class_id='room') 17 | start_instanced = DBOField(False) 18 | 19 | @property 20 | def new_dto(self): 21 | dto = super().new_dto 22 | dto['base_attrs'] = {attr_name: base_attr_value for attr_name in self.attr_list} 23 | return dto 24 | -------------------------------------------------------------------------------- /webclient/editor/panels/article_load.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 7 |
8 |
9 |
10 | 14 |
15 |
-------------------------------------------------------------------------------- /webclient/common/dialogs/alert.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webclient/mud/view/friend_req_msg.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{msgTime(msg)}} 4 | 5 |
6 |
  {{msg.content.friend_name}} wants to be your friend.
7 |
8 | 9 | 10 | 11 |
12 |
-------------------------------------------------------------------------------- /webclient/editor/fragments/aliases.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 |
8 |
9 | 10 | 11 | 13 | 14 |
15 |
-------------------------------------------------------------------------------- /webclient/editor/js/admin.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('MainAdminCtrl', ['$scope', 'lpRemote', 'lpUtil', 2 | function($scope, lpRemote, lpUtil) { 3 | 4 | lpRemote.request('admin/operations', {name: 'list'}).then(function(operations) { 5 | lpUtil.stringSort(operations, 'name'); 6 | $scope.operations = operations; 7 | }); 8 | 9 | $scope.blanks = function(op) { 10 | var blanks = []; 11 | for (var ix = 0; ix < 4 - op.args.length; ix++) { 12 | blanks.push(ix); 13 | } 14 | return blanks; 15 | }; 16 | 17 | $scope.executeOp = function(op) { 18 | lpRemote.request('admin/operations', op).then(function(result) { 19 | $scope.operationResult = result ? result : 'No Content'; 20 | }) 21 | }; 22 | 23 | }]); 24 | -------------------------------------------------------------------------------- /webclient/editor/fragments/script_list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Scripts ({{model.script_refs.length}})
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
{{script.script}}{{script.func_name}}{{script.builder}}
13 |
14 |
15 | -------------------------------------------------------------------------------- /webclient/editor/js/editor_filters.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').service('lpEditFilters', [function() { 2 | 3 | this.hasChild = function (filterType) { 4 | return function (items) { 5 | var result = []; 6 | angular.forEach(items, function (item) { 7 | if (item[filterType + "_list"].length) { 8 | result.push(item); 9 | } 10 | }); 11 | return result; 12 | } 13 | } 14 | }]); 15 | 16 | angular.module('lampost_editor').filter('immTitle', ['lpUtil', 'lpEditor', function(lpUtil, lpEditor) { 17 | 18 | var map = {}; 19 | angular.forEach(lpEditor.constants.imm_levels, function(level, title) { 20 | map[level] = lpUtil.capitalize(title); 21 | }); 22 | 23 | return function(model) { 24 | return map[model.imm_level]; 25 | } 26 | }]); 27 | -------------------------------------------------------------------------------- /webclient/editor/panels/shadow_script.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 8 |
9 |
10 |
11 |
12 | 13 | 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /lampost_setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from lampost.util import logging 4 | from lampost.setup import startargs 5 | 6 | if __name__ != "__main__": 7 | print("Invalid usage") 8 | sys.exit(2) 9 | 10 | 11 | args = startargs.create_parser.parse_args() 12 | 13 | # We set the logging configuration before importing other modules so that the root logger is properly configured 14 | logging.init_config(args) 15 | logging.root_logger.info("Started with args {}", args) 16 | 17 | from lampost.di.resource import register 18 | register('log', logging.LogFactory()) 19 | 20 | from lampost.setup import newsetup 21 | 22 | if args.flush: 23 | response = input("Please confirm the database number you wish to clear: ") 24 | if response != str(args.db_num): 25 | print("Database numbers do not match") 26 | sys.exit(2) 27 | 28 | newsetup.new_setup(args) 29 | -------------------------------------------------------------------------------- /webclient/editor/js/imports_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('ImportsEditorCtrl', ['$scope', 'lpUtil', 'lpDialog', function($scope, lpUtil, lpDialog) { 2 | 3 | 4 | $scope.setDatabaseDialog = function() { 5 | lpDialog.show({templateUrl: "editor/dialogs/select_db.html" , controller: "DbSelectorCtrl", 6 | scope: $scope}); 7 | } 8 | }]); 9 | 10 | 11 | angular.module('lampost_editor').controller('DbSelectorCtrl', ['$scope', 'lpRemote', function($scope, lpRemote) { 12 | $scope.newParams = {db_port: 6379, db_num:0, db_pw: null}; 13 | if ($scope.dbParams) { 14 | angular.copy($scope.dbParams, $scope.newParams); 15 | } 16 | 17 | $scope.setDatabase = function() { 18 | lpRemote.request("editor/imports/set_db", $scope.newParams).then(function(newParams) { 19 | $scope.dbParams = newParams; 20 | $scope.dismiss(); 21 | }) 22 | } 23 | }]); 24 | -------------------------------------------------------------------------------- /lampmud/editor/imports.py: -------------------------------------------------------------------------------- 1 | from lampost.server.handlers import MethodHandler 2 | from lampost.di.app import on_app_start 3 | from lampost.di.resource import Injected, module_inject 4 | from lampost.db.redisstore import RedisStore 5 | 6 | log = Injected('log') 7 | perm = Injected('perm') 8 | ev = Injected('dispatcher') 9 | module_inject(__name__) 10 | 11 | copy_dbs = {} 12 | 13 | 14 | @on_app_start 15 | def _start(): 16 | ev.register('session_disconnect', _remove_db) 17 | 18 | 19 | def _remove_db(session): 20 | if session in copy_dbs: 21 | del copy_dbs[session] 22 | 23 | 24 | class ImportsEditor(MethodHandler): 25 | 26 | def set_db(self): 27 | perm.check_perm(self.player, 'admin') 28 | content = self._content() 29 | db = RedisStore(content.db_host, content.db_port, content.db_num, content.db_pw) 30 | copy_dbs[self.session] = db 31 | del content.db_pw 32 | return content.__dict__ 33 | -------------------------------------------------------------------------------- /webclient/editor/view/input_list.html: -------------------------------------------------------------------------------- 1 |
2 |
{{inputList.name}}
3 |
4 |
5 | 6 |
{{value}}
7 | 9 |
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /lampmud/lpmud/archetype.py: -------------------------------------------------------------------------------- 1 | from lampost.di.config import config_value 2 | from lampost.db.dbo import KeyDBO, OwnerDBO 3 | from lampost.db.dbofield import DBOField, DBOLField 4 | 5 | 6 | class Archetype(OwnerDBO, KeyDBO): 7 | dbo_key_type = 'archetype' 8 | dbo_set_key = 'archetypes' 9 | 10 | name = DBOField("Unnamed") 11 | desc = DBOField('') 12 | base_attrs = DBOField({}) 13 | 14 | 15 | class PlayerRace(Archetype): 16 | dbo_key_type = "race" 17 | dbo_set_key = "races" 18 | 19 | default_skills = DBOField([], 'default_skill') 20 | start_room = DBOLField(dbo_class_id='room') 21 | start_instanced = DBOField(False) 22 | 23 | @classmethod 24 | def new_dto(cls): 25 | dto = super().new_dto() 26 | dto['start_room'] = config_value('default_start_room') 27 | base_val = config_value('base_attr_value') 28 | dto['base_attrs'] = {attr['dbo_id']: base_val for attr in config_value('attributes')} 29 | return dto 30 | 31 | 32 | -------------------------------------------------------------------------------- /webclient/editor/panels/modify_exit.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Modify {{cap(dirMap[activeExit.direction].name)}} Exit x 5 |
6 |
7 | 8 |
9 |
10 | 14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 |
-------------------------------------------------------------------------------- /webclient/editor/view/imports.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 | 19 | {{dbParams.db_host}} 20 | 21 | {{dbParams.db_host}} 22 | 23 | {{dbParams.db_num}} 24 |
25 |
26 |
27 | 28 |
29 | 30 |
31 | 32 | -------------------------------------------------------------------------------- /lampmud/lpmud/newsetup.py: -------------------------------------------------------------------------------- 1 | from lampost.di.config import config_value 2 | 3 | from lampmud.lpmud.archetype import PlayerRace 4 | from lampmud.lpmud import attributes 5 | 6 | 7 | def first_time_setup(args, db): 8 | root_area = config_value('root_area_id') 9 | room_id = "{0}:0".format(root_area) 10 | imm_name = args.imm_name.lower() 11 | 12 | db.create_object('area', {'dbo_id': root_area, 'name': root_area, 'next_room_id': 1}) 13 | db.create_object('room', {'dbo_id': room_id, 'title': "Immortal Start Room", 'desc': "A brand new start room for immortals."}) 14 | 15 | attributes.init() 16 | 17 | race_dto = PlayerRace.new_dto() 18 | race_dto.update(config_value('default_player_race')) 19 | race = db.create_object(PlayerRace, race_dto) 20 | 21 | supreme_level = config_value('imm_levels')['supreme'] 22 | player = db.create_object('player', {'dbo_id': imm_name, 'room_id': room_id, 'race': race.dbo_id, 'home_room': room_id, 'imm_level': supreme_level}) 23 | db.save_object(player) 24 | return player 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /webclient/mud/view/status_tab.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{stat.label}} 5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{opponent.name}}
14 |
15 |
{{stat.label}} 17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /webclient/mud/dialogs/select_character.html: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /lampmud/model/mobile.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbo import CoreDBO, ChildDBO 2 | from lampost.db.dbofield import DBOField 3 | from lampost.db.template import Template 4 | 5 | from lampmud.model.entity import Entity 6 | from lampmud.model.item import target_keys, ItemInstance 7 | 8 | 9 | class MobileTemplate(ChildDBO, Template): 10 | dbo_key_type = 'mobile' 11 | dbo_parent_type = 'area' 12 | 13 | def _on_loaded(self): 14 | self.target_keys = target_keys(self) 15 | 16 | def config_instance(self, instance, room): 17 | instance.attach() 18 | instance.original_env = room 19 | room.mobiles[self].add(instance) 20 | instance.enter_env(room) 21 | 22 | 23 | class Mobile(Entity, ItemInstance): 24 | template_id = 'mobile' 25 | 26 | def _on_detach(self): 27 | self.original_env.mobiles[self.template].remove(self) 28 | 29 | 30 | class MobileReset(CoreDBO): 31 | class_id = 'mobile_reset' 32 | mobile = DBOField(dbo_class_id='mobile', required=True) 33 | reset_key = DBOField(0) 34 | reset_count = DBOField(1) 35 | reset_max = DBOField(1) 36 | -------------------------------------------------------------------------------- /webclient/editor/view/simple_list.html: -------------------------------------------------------------------------------- 1 |
2 |
{{valueSet.name}}
3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 |
{{row}}
14 |
15 |
16 | 18 | 19 |
20 |
21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /webclient/common/dialogs/prompt.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webclient/editor/view/option_list.html: -------------------------------------------------------------------------------- 1 |
2 |
{{valueSet.name}}
3 |
4 |
5 | {{valueSet.rowLabel(row)}} 6 |
{{valueSet.value(row)}}
7 | 8 | 10 | 12 | 13 |
14 |
15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Geoffrey D Genz and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /webclient/editor/panels/feature.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Add Room Feature x 5 |
6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 |
-------------------------------------------------------------------------------- /webclient/editor/dialogs/script_selector.html: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /webclient/editor/dialogs/select_db.html: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /lampmud/lpmud/feature/touchstone.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbofield import DBOField 2 | from lampost.gameops.action import obj_action 3 | from lampmud.model.item import Readable, ItemDBO 4 | 5 | 6 | class Inscription(ItemDBO, Readable): 7 | class_id = 'inscription' 8 | 9 | inscription = Inscription() 10 | inscription.title = "Archaic Inscription" 11 | inscription.text = "Herewith wilt thou be bound" 12 | inscription.desc = "An inscription written in the flowery letters of a time long past." 13 | inscription.on_loaded() 14 | 15 | 16 | class Touchstone(ItemDBO): 17 | class_id = 'touchstone' 18 | 19 | title = DBOField('Touchstone') 20 | desc = DBOField("An unadorned marble obelisk about five feet high. There is an inscription in an archaic script on one side.") 21 | aliases = DBOField(["obelisk"]) 22 | inscription = DBOField(inscription, 'inscription') 23 | 24 | @obj_action() 25 | def touch(self, source, **_): 26 | source.display_line("You feel a shock coursing through you. It lasts a few seconds") 27 | source.touchstone = self.dbo_owner.dbo_id 28 | 29 | def _on_loaded(self): 30 | self.target_providers = [self.inscription] 31 | self.instance_providers.append(self.inscription) 32 | -------------------------------------------------------------------------------- /webclient/editor/view/edit_list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{editorLabel()}}: {{editorTitle()}} 4 | 6 |    7 | 8 |
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
{{col.header}}
{{col.data(model)}}
23 |
24 |
25 |
-------------------------------------------------------------------------------- /webclient/editor/panels/modify_script.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Modify Shadow Script x 4 |
5 |
6 | 7 |
8 |
9 |
10 | 11 |
{{addObj.func_name}}
12 |
13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |
21 | 22 |
23 | 26 | 28 |
29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /lampmud/editor/shared.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.db.exceptions import DataError 3 | from lampost.editor.editor import Editor 4 | from lampmud.comm.broadcast import BroadcastMap, Broadcast, broadcast_types 5 | 6 | mud_actions = Injected('mud_actions') 7 | module_inject(__name__) 8 | 9 | 10 | class SocialsEditor(Editor): 11 | def __init__(self): 12 | super().__init__('social') 13 | 14 | @staticmethod 15 | def preview(source, target, b_map, self_source, **_): 16 | broadcast = Broadcast(BroadcastMap(**b_map), source, source if self_source else target) 17 | return {broadcast_type['id']: broadcast.substitute(broadcast_type['id']) for broadcast_type in broadcast_types} 18 | 19 | def _pre_create(self, obj_def, *_): 20 | if mud_actions.primary(obj_def['dbo_id']): 21 | raise DataError("Verb already in use") 22 | 23 | 24 | def _ensure_name(obj_def): 25 | name = obj_def['name'] or obj_def['verb'] or obj_def['dbo_id'] 26 | obj_def['name'] = name.capitalize() 27 | 28 | 29 | class SkillEditor(Editor): 30 | def _pre_create(self, obj_def, *_): 31 | _ensure_name(obj_def) 32 | 33 | def _pre_update(self, obj_def, *_): 34 | _ensure_name(obj_def) 35 | 36 | -------------------------------------------------------------------------------- /lampmud/env/instancemgr.py: -------------------------------------------------------------------------------- 1 | from lampost.di.app import on_app_start 2 | from lampost.di.config import ConfigVal 3 | from lampost.di.resource import Injected, module_inject 4 | 5 | from lampmud.env.instance import AreaInstance 6 | 7 | db = Injected('datastore') 8 | ev = Injected('dispatcher') 9 | module_inject(__name__) 10 | 11 | preserve_hours = ConfigVal('instance_preserve_hours') 12 | 13 | instance_map = {} 14 | 15 | 16 | @on_app_start 17 | def _start(): 18 | ev.register('maintenance', remove_old) 19 | 20 | 21 | def next_instance(): 22 | instance_id = db.db_counter('instance_id') 23 | area_instance = AreaInstance(instance_id) 24 | instance_map[instance_id] = area_instance 25 | return area_instance 26 | 27 | 28 | def remove_old(): 29 | stale_pulse = ev.future_pulse(preserve_hours.value * 60 * 60) 30 | for instance_id, instance in instance_map.copy().items(): 31 | if instance.pulse_stamp < stale_pulse and not [entity for entity in instance.entities 32 | if hasattr(entity, 'is_player') and entity.session]: 33 | delete(instance_id) 34 | 35 | 36 | def get(instance_id): 37 | return instance_map.get(instance_id) 38 | 39 | 40 | def delete(instance_id): 41 | del instance_map[instance_id] 42 | -------------------------------------------------------------------------------- /webclient/editor/view/value_set.html: -------------------------------------------------------------------------------- 1 |
2 |
{{valueSet.name}}
3 |
4 | 5 |
7 | {{valueSet.rowLabel(row)}} 8 | 10 | 11 | 13 | 14 |
15 |
16 |
17 | 19 | 20 |
21 |
22 | 23 |
24 | -------------------------------------------------------------------------------- /lampmud/env/movement.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbo import CoreDBO 2 | 3 | 4 | class Direction(CoreDBO): 5 | class_id = 'direction' 6 | ref_map = {} 7 | ordered = [] 8 | 9 | @classmethod 10 | def load_ref(cls, dbo_id, owner=None): 11 | return cls.ref_map[dbo_id] if dbo_id else None 12 | 13 | @classmethod 14 | def find_dir(cls, name): 15 | for key, value in cls.ref_map.items(): 16 | if name == key or name == value.desc: 17 | return value 18 | 19 | def __init__(self, dbo_id, desc, rev_key): 20 | self.dbo_id = dbo_id 21 | self.desc = desc 22 | self.rev_key = rev_key 23 | Direction.ref_map[dbo_id] = self 24 | Direction.ordered.append({'dbo_id':dbo_id, 'name':desc, 'rev_key': rev_key}) 25 | 26 | @property 27 | def rev_dir(self): 28 | return Direction.ref_map[self.rev_key] 29 | 30 | 31 | NORTH = Direction('n', 'north', 's') 32 | SOUTH = Direction('s', 'south', 'n') 33 | EAST = Direction('e', 'east', 'w') 34 | WEST = Direction('w', 'west', 'e') 35 | UP = Direction('u', 'up', "d") 36 | DOWN = Direction('d', 'down', "u") 37 | NE = Direction('ne', 'northeast', 'sw') 38 | SE = Direction('se', 'southeast', 'nw') 39 | NW = Direction('nw', 'northwest', 'se') 40 | SW = Direction('sw', 'southwest', 'ne') 41 | -------------------------------------------------------------------------------- /webclient/editor/view/editor_main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
{{parentType}}  {{parentLabel}} {{editorLabel}}: {{modelName()}} 6 |
7 | 9 |   10 | 12 |   13 | 15 |
16 |
17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /webclient/mud/dialogs/password_reset.html: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /webclient/mud/dialogs/forgot_password.html: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /webclient/mud/dialogs/forgot_name.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webclient/mud/js/status.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_mud').controller('StatusTabCtrl', ['$scope', 'lpData', 'lpEvent', 2 | function($scope, lpData, lpEvent) { 3 | 4 | $scope.statsList = ['health', 'mental', 'stamina', 'action']; 5 | $scope.stats = {action: {label: 'Action'}, 6 | health: {label: 'Health'}, 7 | stamina: {label: 'Stamina'}, 8 | mental: {label: 'Mental'}}; 9 | $scope.oppStats = angular.copy($scope.stats); 10 | 11 | updateStatus(lpData.status); 12 | 13 | lpEvent.register('status', updateStatus, $scope); 14 | 15 | function updateStatus(status) { 16 | $scope.status = status; 17 | updateBars($scope.stats, status); 18 | $scope.opponent = status.opponent; 19 | if ($scope.opponent) { 20 | updateBars($scope.oppStats, $scope.opponent); 21 | } 22 | } 23 | 24 | function updateBars(barStats, rawStats) { 25 | angular.forEach($scope.statsList, function(key) { 26 | var stats = barStats[key]; 27 | var perc = Math.ceil(100 * rawStats[key] / rawStats['base_' + key]).toString(); 28 | stats.style = {width: perc.toString() + '%'}; 29 | if (perc < 20) { 30 | stats.class = 'progress-bar-danger'; 31 | } else if (perc < 50) { 32 | stats.class = 'progress-bar-warning'; 33 | } else { 34 | stats.class = 'progress-bar-success'; 35 | } 36 | }); 37 | } 38 | 39 | }]); 40 | -------------------------------------------------------------------------------- /lampmud/server/messages.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.server.link import link_route 3 | 4 | um = Injected('user_manager') 5 | friend_service = Injected('friend_service') 6 | message_service = Injected('message_service') 7 | module_inject(__name__) 8 | 9 | 10 | @link_route('messages/friend_response') 11 | def friend_response(source_id, player_id, action, msg_id, **_): 12 | friend_service.remove_request(source_id, player_id) 13 | message_service.remove_message(player_id, msg_id) 14 | if action == 'accept': 15 | friend_service.add_friend(source_id, player_id) 16 | message_service.add_message("system", "{} has accepted your friend request.".format(um.id_to_name(player_id)), source_id) 17 | message_service.add_message("system", "You have accepted {}'s friend request.".format(um.id_to_name(source_id)), player_id) 18 | elif action == 'block': 19 | message_service.block_messages(player_id, source_id) 20 | else: 21 | message_service.add_message("system", "{} has declined your friend request.".format(um.id_to_name(player_id)), source_id) 22 | message_service.add_message("system", "You have declined {}'s friend request.".format(um.id_to_name(source_id)), player_id) 23 | 24 | 25 | @link_route('messages/delete') 26 | def message_delete(player_id, msg_id, **_): 27 | message_service.remove_message(player_id, msg_id) 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /lampmud/mud/inventory.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbofield import DBOField 2 | 3 | from lampmud.model.item import ItemDBO 4 | from lampmud.mud.action import mud_action 5 | 6 | 7 | @mud_action(('get', 'pick up'), 'get', target_class="env_items", quantity=True) 8 | def get(source, target, quantity=None): 9 | target.get(source, quantity) 10 | 11 | 12 | @mud_action(('drop', 'put down'), 'drop', target_class="inven", quantity=True) 13 | def drop(source, target, quantity=None): 14 | target.drop(source, quantity) 15 | 16 | 17 | @mud_action(('i', 'inventory')) 18 | def inven(source): 19 | if source.inven: 20 | source.display_line("You are carrying:") 21 | for article in source.inven: 22 | source.display_line("  {}".format(article.short_desc(source))) 23 | else: 24 | source.display_line("You aren't carrying anything.") 25 | 26 | 27 | class InvenContainer(ItemDBO): 28 | class_id = 'container' 29 | 30 | contents = DBOField([], 'untyped') 31 | 32 | def __iter__(self): 33 | for item in self.contents: 34 | yield item 35 | 36 | def __len__(self): 37 | return len(self.contents) 38 | 39 | def __contains__(self, item): 40 | return item in self.contents 41 | 42 | def __getitem__(self, key): 43 | return self.contents[key] 44 | 45 | def append(self, item): 46 | self.contents.append(item) 47 | 48 | def remove(self, item): 49 | self.contents.remove(item) 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lampmud/setup/update.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.editor.admin import admin_op 3 | 4 | log = Injected('log') 5 | db = Injected('datastore') 6 | ev = Injected('dispatcher') 7 | module_inject(__name__) 8 | 9 | 10 | @admin_op 11 | def assign_race(race_id): 12 | race = db.load_object(race_id, 'race') 13 | updates = 0 14 | if not race: 15 | return "Invalid race specified" 16 | for player in db.load_object_set('player'): 17 | if not player.race: 18 | log.info("Assigning race {} to {}", race_id, player.name) 19 | player.race = race 20 | db.save_object(player) 21 | updates += 1 22 | return "{} players updated.".format(updates) 23 | 24 | 25 | def test_memory(*_): 26 | for area_ix in range(1000): 27 | area_id = 'perf_test{}'.format(area_ix) 28 | old_area = db.load_object(area_id, 'area') 29 | if old_area: 30 | db.delete_object(old_area) 31 | area_dbo = {'dbo_id': area_id} 32 | db.create_object('area', area_dbo) 33 | for dbo_id in range(100): 34 | room = {'dbo_id': '{}:{}'.format(area_id, dbo_id), 'desc': area_id * 40, 'title': area_id * 2} 35 | db.create_object('room', room) 36 | 37 | 38 | def clean_perf(*_): 39 | for area_ix in range(1000): 40 | area_id = 'perf_test{}'.format(area_ix) 41 | old_area = db.load_object(area_id, 'area') 42 | if old_area: 43 | db.delete_object(old_area) 44 | 45 | -------------------------------------------------------------------------------- /webclient/editor/panels/touchstone.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Modify Touchstone x 5 |
6 |
7 | 8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 |
-------------------------------------------------------------------------------- /lampmud/mud/action.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject, register 2 | from lampost.gameops.action import make_action, ActionError, ActionCache 3 | 4 | log = Injected('log') 5 | module_inject(__name__) 6 | 7 | _mud_actions = ActionCache() 8 | register('mud_actions', _mud_actions) 9 | 10 | imm_actions = set() 11 | 12 | 13 | def mud_action(verbs, msg_class=None, **kwargs): 14 | def dec_wrapper(func): 15 | action = make_action(func, verbs, msg_class, **kwargs) 16 | _mud_actions.add_unique(action) 17 | return dec_wrapper 18 | 19 | 20 | def imm_action(verbs, msg_class=None, imm_level='builder', **kwargs): 21 | def dec_wrapper(func): 22 | imm_actions.add(func) 23 | func.imm_level = imm_level 24 | return make_action(func, verbs, msg_class, **kwargs) 25 | return dec_wrapper 26 | 27 | 28 | @mud_action('help', target_class='cmd_str_opt') 29 | def help_action(source, target): 30 | if not target: 31 | source.display_line('Available actions:') 32 | verb_lists = ["/".join(verbs) for verbs in _mud_actions.all_actions().values()] 33 | return source.display_line(", ".join(sorted(verb_lists))) 34 | actions = _mud_actions.primary(target) 35 | if not actions: 36 | actions = _mud_actions.abbrev(target) 37 | if not actions: 38 | raise ActionError("No matching command found") 39 | if len(actions) > 1: 40 | raise ActionError("Multiple matching actions") 41 | return getattr(actions[0], "help_text", "No help available.") 42 | -------------------------------------------------------------------------------- /webclient/editor/view/area.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 8 |
9 |
10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 |
18 | 20 |
21 |
22 | 23 |
24 |
25 | 27 |
28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 |
38 | 40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /lampmud/lpmud/article.py: -------------------------------------------------------------------------------- 1 | from lampost.gameops.script import Scriptable 2 | 3 | from lampmud.comm.broadcast import BroadcastMap 4 | from lampost.meta.auto import TemplateField 5 | from lampost.db.dbofield import DBOField, DBOTField 6 | from lampmud.model.article import Article, ArticleTemplate 7 | from lampmud.mud.action import mud_action 8 | 9 | 10 | class ArticleTemplateLP(ArticleTemplate): 11 | class_id = 'article' 12 | 13 | remove_msg = BroadcastMap(s="You unequip {N}", e="{n} unequips {N}") 14 | equip_msg = BroadcastMap(s="You wear {N}", e="{n} wears {N}") 15 | wield_msg = BroadcastMap(s="You wield {N}", e="{n} wields {N}") 16 | 17 | def _on_loaded(self): 18 | if self.art_type == 'weapon': 19 | self.equip_msg = self.wield_msg 20 | 21 | 22 | class ArticleLP(Article, Scriptable): 23 | equip_slot = DBOTField() 24 | current_slot = DBOField() 25 | 26 | weapon_type = DBOTField('mace') 27 | damage_type = DBOTField('blunt') 28 | delivery = DBOTField('melee') 29 | equip_msg = TemplateField() 30 | remove_msg = TemplateField() 31 | 32 | def on_equipped(self, equipper): 33 | equipper.broadcast(target=self, broadcast_map=self.equip_msg) 34 | 35 | def on_removed(self, remover): 36 | remover.broadcast(target=self, broadcast_map=self.remove_msg) 37 | 38 | 39 | @mud_action(('wear', 'equip', 'wield'), 'equip_slot', target_class="inven") 40 | def wear(source, target, **_): 41 | source.equip_article(target) 42 | 43 | 44 | @mud_action(('remove', 'unequip', 'unwield'), 'current_slot') 45 | def remove(source, target, **_): 46 | source.remove_article(target) 47 | -------------------------------------------------------------------------------- /webclient/editor/js/area_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('MobileEditorCtrl', ['$scope', 'lpEditorTypes', 'lpSkillService', 2 | function ($scope, lpEditorTypes, lpSkillService) { 3 | 4 | var skillSet = new lpEditorTypes.ValueObjList('default_skills', "Default Skills [Level]", 'skill_template', 'skill_level'); 5 | skillSet.options = lpSkillService.allSkills(); 6 | skillSet.optionKey = 'dbo_key'; 7 | $scope.skillSet = skillSet; 8 | 9 | }]); 10 | 11 | 12 | angular.module('lampost_editor').controller('ArticleEditorCtrl', ['$scope', 'lpEvent', 'lpEditor', 13 | function ($scope, lpEvent, lpEditor) { 14 | 15 | $scope.locals = {}; 16 | $scope.equip_slots = angular.copy($scope.constants.equip_slots); 17 | $scope.equip_slots.unshift('[none]'); 18 | $scope.addPanel = null; 19 | 20 | $scope.syncSlot = function() { 21 | $scope.model.equip_slot = $scope.locals.equipSlot == '[none]' ? null : $scope.locals.equipSlot; 22 | }; 23 | 24 | lpEvent.register('editReady', function() { 25 | $scope.locals.equipSlot = lpEditor.original.equip_slot ? lpEditor.original.equip_slot : '[none]'; 26 | $scope.closeAdd(); 27 | }); 28 | 29 | $scope.addScript = function(event) { 30 | if (event) { 31 | event.stopImmediatePropagation(); 32 | event.preventDefault(); 33 | } 34 | $scope.addPanel = 'editor/panels/new_script.html'; 35 | }; 36 | 37 | $scope.modifyScript = function(scriptRef) { 38 | lpEditor.addObj = scriptRef; 39 | $scope.addPanel = 'editor/panels/modify_script.html'; 40 | }; 41 | 42 | $scope.closeAdd = function() { 43 | $scope.addPanel = null; 44 | } 45 | 46 | 47 | }]); 48 | -------------------------------------------------------------------------------- /lampmud/lpmud/attributes.py: -------------------------------------------------------------------------------- 1 | from lampost.di.app import on_app_start 2 | from lampost.di.config import config_value 3 | from lampost.db.dbofield import DBOField 4 | from lampost.db.registry import get_dbo_class 5 | 6 | attributes = [] 7 | attr_list = [] 8 | 9 | pool_keys = [] 10 | resource_pools = [] 11 | 12 | 13 | @on_app_start(priority=200) 14 | def init(): 15 | global attributes 16 | global resource_pools 17 | global pool_keys 18 | global attr_list 19 | 20 | attributes = config_value('attributes') 21 | attr_list = [attr['dbo_id'] for attr in attributes] 22 | 23 | resource_pools = config_value('resource_pools') 24 | pool_keys = [(pool['dbo_id'], "base_{}".format(pool['dbo_id'])) for pool in resource_pools] 25 | 26 | player_cls = get_dbo_class('player') 27 | player_cls.add_dbo_fields({attr['dbo_id']: DBOField(0) for attr in attributes}) 28 | player_cls.add_dbo_fields({pool['dbo_id']: DBOField(0) for pool in resource_pools}) 29 | 30 | 31 | def base_pools(entity): 32 | for pool in resource_pools: 33 | total = sum(getattr(entity, key) * value for key, value in pool['calc'].items()) 34 | setattr(entity, 'base_{}'.format(pool['dbo_id']), total) 35 | 36 | 37 | def fill_pools(entity): 38 | base_pools(entity) 39 | for pool_id, base_pool_id in pool_keys: 40 | setattr(entity, pool_id, getattr(entity, base_pool_id)) 41 | 42 | 43 | def restore_attrs(entity): 44 | for attr in attr_list: 45 | setattr(entity, attr, getattr(entity, "perm_{}".format(attr))) 46 | 47 | 48 | def need_refresh(entity): 49 | for pool_id, base_pool_id in pool_keys: 50 | if getattr(entity, pool_id) < getattr(entity, base_pool_id): 51 | return True 52 | -------------------------------------------------------------------------------- /lampmud/mud/chat.py: -------------------------------------------------------------------------------- 1 | from lampost.gameops.action import ActionError 2 | from lampost.di.resource import Injected, module_inject 3 | from lampmud.mud.action import mud_action 4 | 5 | sm = Injected('session_manager') 6 | module_inject(__name__) 7 | 8 | 9 | @mud_action('emote', target_class='cmd_str') 10 | def emote(source, target): 11 | source.broadcast(raw="{}{} {}".format('' if source.imm_level else ':', source.name, target)) 12 | 13 | 14 | @mud_action('tell', target_class="player_online", obj_class="cmd_str") 15 | def tell(source, target, obj): 16 | tell_message(source, target, obj) 17 | 18 | 19 | def tell_message(source, player, statement): 20 | if not statement: 21 | return source.display_line("Say what to " + player.name + "?") 22 | player.last_tell = source.dbo_id 23 | player.display_line(source.name + " tells you, `" + statement + "'", 'tell_from') 24 | source.display_line("You tell " + player.name + ", `" + statement + "'", 'tell_to') 25 | 26 | 27 | @mud_action('reply', target_class='cmd_str') 28 | def reply(source, target): 29 | if not source.last_tell: 30 | raise ActionError("You have not received a tell recently.") 31 | session = sm.player_session(source.last_tell) 32 | if session: 33 | tell_message(source, session.player, target) 34 | else: 35 | source.last_tell = None 36 | return source.display_line("{} is no longer logged in".format(source.last_tell)) 37 | 38 | 39 | @mud_action('say', target_class='cmd_str') 40 | def say(source, target): 41 | source.display_line("You say, `{}'".format(target), display='say') 42 | source.broadcast(raw="{} says, `{}'".format(source.name, target), 43 | display='say', silent=True) 44 | -------------------------------------------------------------------------------- /webclient/editor/view/admin_view.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
Available Operations
6 |
7 | 8 | 9 | 10 | 13 | 19 | 22 | 25 | 26 |
11 | {{op.name}} 12 | 14 |
15 | {{arg}} 16 | 17 |
18 |
20 | 21 | 23 | 24 |
27 |
28 |
29 | {{operationResult}} 30 |
31 | 34 |
35 |
36 |
37 |
38 |
39 |
-------------------------------------------------------------------------------- /lampmud/lpmud/combat/system.py: -------------------------------------------------------------------------------- 1 | import bisect 2 | 3 | from lampost.di.resource import Injected, module_inject 4 | 5 | ev = Injected('dispatcher') 6 | module_inject(__name__) 7 | 8 | _pulse_map = {} 9 | 10 | 11 | def add_action(source, action, pulse, callback, priority=0): 12 | if not pulse: 13 | process_immediate(source, action, callback) 14 | return 15 | pulse_key = ev.current_pulse + pulse 16 | current_diff = pulse 17 | sys_action = SystemAction(source, action, priority, callback) 18 | 19 | try: 20 | pulse_actions = _pulse_map[pulse_key] 21 | except KeyError: 22 | _pulse_map[pulse_key] = pulse_actions = [] 23 | ev.register_once(process_actions, current_diff, kwargs={'pulse_key': pulse_key}) 24 | bisect.insort_right(pulse_actions, sys_action) 25 | 26 | 27 | def process_actions(pulse_key): 28 | affected = set() 29 | actions = _pulse_map[pulse_key] 30 | for sys_action in actions: 31 | affected.add(sys_action.source) 32 | sys_action.callback(sys_action.action, affected) 33 | del _pulse_map[pulse_key] 34 | for entity in affected: 35 | try: 36 | entity.resolve_actions() 37 | except AttributeError: 38 | pass 39 | 40 | 41 | def process_immediate(source, action, callback): 42 | affected = {source} 43 | callback(action, affected) 44 | for entity in affected: 45 | try: 46 | entity.resolve_actions() 47 | except AttributeError: 48 | pass 49 | 50 | 51 | class SystemAction: 52 | def __init__(self, source, action, priority, callback): 53 | self.source = source 54 | self.action = action 55 | self.priority = priority 56 | self.callback = callback 57 | 58 | def __lt__(self, other): 59 | return self.priority < other.priority 60 | -------------------------------------------------------------------------------- /lampmud/mud/socials.py: -------------------------------------------------------------------------------- 1 | from lampost.di.app import on_app_start 2 | 3 | from lampmud.comm.broadcast import BroadcastMap 4 | from lampost.di.resource import Injected, module_inject 5 | from lampost.db.dbo import KeyDBO, OwnerDBO 6 | from lampost.db.dbofield import DBOField 7 | from lampost.gameops.action import make_action 8 | from lampmud.mud.action import mud_action 9 | 10 | log = Injected('log') 11 | db = Injected('datastore') 12 | mud_actions = Injected('mud_actions') 13 | module_inject(__name__) 14 | 15 | all_socials = {} 16 | 17 | 18 | @on_app_start 19 | def _start(): 20 | for social in db.load_object_set('social'): 21 | add_social_action(social) 22 | 23 | 24 | def add_social_action(social): 25 | if mud_actions.primary(social.dbo_id): 26 | log.warn("Mud action already exists for social id {}", social.dbo_id) 27 | else: 28 | mud_actions.add(make_action(social, social.dbo_id)) 29 | all_socials[social.dbo_id] = social 30 | 31 | 32 | def remove_social_action(social): 33 | all_socials.pop(social.dbo_id) 34 | mud_actions.remove(social) 35 | 36 | 37 | class Social(KeyDBO, OwnerDBO): 38 | dbo_key_type = 'social' 39 | dbo_set_key = 'socials' 40 | 41 | b_map = DBOField({}) 42 | 43 | msg_class = 'social' 44 | 45 | def _on_loaded(self): 46 | self.broadcast_map = BroadcastMap(**self.b_map) 47 | 48 | def _on_db_created(self): 49 | add_social_action(self) 50 | 51 | def _on_db_deleted(self): 52 | remove_social_action(self) 53 | 54 | def __call__(self, source, target, **_): 55 | source.broadcast(target=target, broadcast_map=self.broadcast_map) 56 | 57 | 58 | @mud_action('socials') 59 | def socials_action(**_): 60 | socials = sorted(all_socials.keys()) 61 | if socials: 62 | return " ".join(socials) 63 | return "No socials created!" 64 | -------------------------------------------------------------------------------- /lampmud/lpmud/env.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbofield import DBOField 2 | from lampost.di.app import on_app_start 3 | from lampost.gameops.action import ActionError 4 | from lampost.di.config import on_config_change, config_value 5 | 6 | from lampmud.env.room import Exit 7 | 8 | exit_cost_map = {} 9 | prep_multiplier = 1 10 | 11 | 12 | @on_app_start 13 | @on_config_change 14 | def _config(): 15 | global room_stamina 16 | global room_action 17 | global default_room_size 18 | room_stamina = config_value('room_stamina') 19 | room_action = config_value('room_action') 20 | default_room_size = config_value('default_room_size') 21 | 22 | 23 | def find_cost(room): 24 | if room and room.size: 25 | try: 26 | return exit_cost_map[room.size] 27 | except KeyError: 28 | exit_cost_map[room.size] = {'action': int(room_stamina * room.size / default_room_size), 29 | 'stamina': int(room_action * room.size / default_room_size)} 30 | return exit_cost_map[room.size] 31 | 32 | 33 | class ExitLP(Exit): 34 | class_id = 'exit' 35 | 36 | guarded = DBOField(False) 37 | door_key = DBOField() 38 | 39 | @property 40 | def prep_time(self): 41 | return prep_multiplier * self.dbo_owner.size // 10 42 | 43 | def prepare_action(self, source): 44 | if self.guarded: 45 | guards = [guard for guard in source.env.denizens if guard.affinity != source.affinity] 46 | if guards: 47 | raise ActionError(guards[0].guard_msg.format(source=guards[0].name, exit=self._dir.desc)) 48 | source.display_line("You head {}.".format(self._dir.desc)) 49 | source.env.allow_leave(source, self) 50 | 51 | def _move_user(self, source): 52 | source.apply_costs(find_cost(self.dbo_owner)) 53 | super()._move_user(source) 54 | -------------------------------------------------------------------------------- /webclient/editor/js/player_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('PlayerEditorCtrl', ['$scope', 'lpCache', 'lpEditor', 'lpEvent', 'lpEditorTypes', 2 | function ($scope, lpCache, lpEditor, lpEvent, lpEditorTypes) { 3 | 4 | var prevLevel, model; 5 | 6 | function setPerms() { 7 | model = $scope.model; 8 | $scope.canPromote = model.can_write && lpEditor.playerId != model.dbo_id && lpEditor.immLevel >= lpEditor.constants.imm_levels.admin; 9 | prevLevel = $scope.model.imm_level; 10 | } 11 | 12 | $scope.checkPromote = function() { 13 | var error; 14 | if (model.logged_in == 'Yes') { 15 | error = "Please do that in game."; 16 | } else if (lpEditor.immLevel <= model.imm_level && lpEditor.immLevel < lpEditor.constants.imm_levels.supreme) { 17 | error = "You cannot promote to that level!" 18 | } 19 | if (error) { 20 | $scope.errors.promote = error; 21 | $scope.model.imm_level = prevLevel; 22 | } else { 23 | $scope.errors.promote = null; 24 | prevLevel = $scope.model.imm_level; 25 | } 26 | }; 27 | 28 | lpCache.cache('race').then(function(races) { 29 | $scope.races = races; 30 | }); 31 | 32 | $scope.$on('$destroy', function() { 33 | lpCache.deref('race'); 34 | }); 35 | 36 | lpEvent.register('editReady', setPerms); 37 | 38 | $scope.playerRoomSelect = new lpEditorTypes.ChildSelect('room_id', 'room'); 39 | 40 | }]); 41 | 42 | 43 | angular.module('lampost_editor').controller('UserEditorCtrl', ['$scope', 'lpEvent', 'lpEditor', 'lpCache', 44 | function ($scope, lpEvent, lpEditor, lpCache) { 45 | 46 | $scope.editPlayer = function(player_id) { 47 | var playerModel = lpCache.cachedValue('player:' + player_id); 48 | if (playerModel) { 49 | lpEvent.dispatchLater('startEdit', playerModel); 50 | } 51 | } 52 | 53 | }]); 54 | -------------------------------------------------------------------------------- /webclient/editor/panels/extra.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{newAdd ? 'Create': 'Modify'}} Extra x 4 |
5 |
6 |
7 |
8 |
9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 |
25 |
26 |
27 | 28 | {{lastError}} 29 |
30 |
31 |
32 |
33 |
34 | 37 | 40 | 42 |
43 |
44 |
45 |
46 |
-------------------------------------------------------------------------------- /webclient/editor/view/script.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 | 8 | 9 |
{{idOnly(model)}}
10 |
11 |
12 |
13 |
14 | 15 | 16 |
{{model.dbo_ts | dbDate}}
17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 | 31 |
32 |
33 |
34 |
35 | 36 | 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 |
52 | 53 |
54 | 55 | -------------------------------------------------------------------------------- /lampmud/editor/scripts.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from lampost.editor.editor import ChildrenEditor 4 | 5 | from lampost.di.resource import Injected, module_inject 6 | from lampost.db.exceptions import DataError 7 | from lampost.gameops.script import compile_script 8 | 9 | db = Injected('datastore') 10 | perm = Injected('perm') 11 | module_inject(__name__) 12 | 13 | 14 | def _validate(obj_def): 15 | hasher = hashlib.md5() 16 | hasher.update(obj_def['text'].encode()) 17 | obj_def['script_hash'] = hasher.hexdigest() 18 | _, err_str = compile_script(obj_def['script_hash'], obj_def['text'], obj_def['title']) 19 | if err_str: 20 | raise DataError(err_str) 21 | 22 | 23 | class ScriptEditor(ChildrenEditor): 24 | def __init__(self): 25 | super().__init__('script') 26 | 27 | def _pre_create(self, obj_def, session): 28 | _validate(obj_def) 29 | obj_def['approved'] = perm.has_perm(session.player, 'admin') and obj_def['approved'] 30 | 31 | def _pre_update(self, obj_def, existing, session): 32 | _validate(obj_def) 33 | holder_keys = db.fetch_set_keys('{}:holders'.format(existing.dbo_key)) 34 | if obj_def['builder'] == 'shadow': 35 | cls_type = obj_def['metadata']['cls_type'] 36 | cls_shadow = obj_def['metadata']['cls_shadow'] 37 | errors = [] 38 | for dbo_key in holder_keys: 39 | holder = db.load_object(dbo_key) 40 | for script_ref in getattr(holder, "script_refs", ()): 41 | if script_ref.script.dbo_id == existing.dbo_id: 42 | if cls_type != holder.class_id: 43 | errors.append("{} wrong class id {}".format(holder.dbo_id, holder.class_id)) 44 | elif script_ref.func_name != cls_shadow: 45 | errors.append("{} wrong function {}".format(holder.dbo_id, script_ref.func_name)) 46 | if errors: 47 | raise DataError("Incompatible usages must be removed first: {}".format(" ".join(errors))) 48 | if obj_def['script_hash'] != existing.script_hash: 49 | obj_def['approved'] = perm.has_perm(session.player, 'admin') and obj_def['approved'] 50 | 51 | -------------------------------------------------------------------------------- /lampmud/model/item.py: -------------------------------------------------------------------------------- 1 | from lampost.event.zone import Attachable 2 | from lampost.gameops.target import TargetKeys 3 | from lampost.meta.auto import TemplateField 4 | from lampost.db.dbo import CoreDBO, DBOAspect 5 | from lampost.db.dbofield import DBOField, DBOTField 6 | from lampost.db.template import TemplateInstance 7 | from lampost.gameops.action import obj_action, ActionProvider 8 | 9 | 10 | def target_keys(item): 11 | t_keys = TargetKeys(item.title) 12 | for alias in item.aliases: 13 | t_keys.add(alias) 14 | return t_keys 15 | 16 | 17 | class ItemAspect(DBOAspect, ActionProvider, Attachable): 18 | sex = DBOField('none') 19 | flags = DBOField({}) 20 | title = '' 21 | aliases = [] 22 | 23 | living = False 24 | env = None 25 | 26 | def _on_loaded(self): 27 | if not self.target_keys: 28 | self.target_keys = target_keys(self) 29 | 30 | @property 31 | def name(self): 32 | return self.title 33 | 34 | def short_desc(self, observer=None): 35 | return self.title 36 | 37 | def long_desc(self, observer): 38 | return self.desc if self.desc else self.title 39 | 40 | def examine(self, source): 41 | if source.can_see(self): 42 | source.display_line(self.long_desc(source)) 43 | 44 | def glance(self, source): 45 | if source.can_see(self): 46 | source.display_line(self.short_desc(source)) 47 | 48 | def receive_broadcast(self, broadcast): 49 | pass 50 | 51 | def social(self, social): 52 | pass 53 | 54 | def leave_env(self): 55 | pass 56 | 57 | 58 | class ItemDBO(CoreDBO, ItemAspect): 59 | class_id = 'base_item' 60 | 61 | desc = DBOField('') 62 | title = DBOField('') 63 | aliases = DBOField([]) 64 | 65 | target_keys = None 66 | 67 | 68 | class ItemInstance(TemplateInstance, ItemAspect): 69 | desc = DBOTField('') 70 | title = DBOTField('') 71 | aliases = DBOTField([]) 72 | 73 | target_keys = TemplateField() 74 | 75 | 76 | class Readable(DBOAspect, ActionProvider): 77 | class_id = 'readable' 78 | 79 | text = DBOField('') 80 | 81 | @obj_action() 82 | def read(self, source): 83 | source.display_line(self.text, "tell_to") 84 | -------------------------------------------------------------------------------- /webclient/editor/view/user.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 |
{{model.dbo_id}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Players ({{model.player_ids.length}})
51 | 52 | 53 | 54 | 55 | 56 |
{{cap(playerId)}}
57 |
58 |
59 | 60 |
61 |
62 | -------------------------------------------------------------------------------- /webclient/editor/js/socials_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('SocialEditorCtrl', ['$scope', 'lpRemote', 'lpEditor', 'lpEvent', 2 | function ($scope, lpRemote, lpEditor, lpEvent) { 3 | 4 | var preview = {}; 5 | var bTypeIds = []; 6 | var sourceSelf = false; 7 | 8 | $scope.bTypeGrid = []; 9 | 10 | angular.forEach(lpEditor.constants.broadcast_types, function (bType) { 11 | $scope.bTypeGrid[bType.grid_x] = $scope.bTypeGrid[bType.grid_x] || []; 12 | $scope.bTypeGrid[bType.grid_x][bType.grid_y] = bType; 13 | bTypeIds.push(bType.id); 14 | }); 15 | 16 | $scope.editMode = true; 17 | $scope.source = 'Player'; 18 | $scope.target = 'Target'; 19 | $scope.displayMap = {}; 20 | 21 | $scope.startEditMode = function () { 22 | $scope.editMode = true; 23 | updateDisplayMap(); 24 | }; 25 | 26 | $scope.toggleTarget = function () { 27 | sourceSelf = !sourceSelf; 28 | $scope.targetTitle = sourceSelf ? "Target is self." : 'Target is other.'; 29 | $scope.targetClass = 'fa ' + (sourceSelf ? 'fa-reply-all' : 'fa-long-arrow-right') 30 | }; 31 | 32 | $scope.previewSocial = function () { 33 | lpRemote.request('editor/social/preview', { 34 | target: $scope.target, self_source: sourceSelf, 35 | source: $scope.source, b_map: $scope.model.b_map 36 | }).then(function (data) { 37 | preview = data; 38 | preview.dbo_id = $scope.model.dbo_id; 39 | $scope.editMode = false; 40 | updateDisplayMap(); 41 | }); 42 | }; 43 | 44 | $scope.updateSocial = function (bType) { 45 | $scope.model.b_map[bType] = $scope.displayMap[bType]; 46 | }; 47 | 48 | function updateDisplayMap() { 49 | if ($scope.model.b_map) { 50 | angular.forEach(bTypeIds, function (key) { 51 | $scope.displayMap[key] = $scope.editMode ? $scope.model.b_map[key] : preview[key]; 52 | }); 53 | } 54 | } 55 | 56 | lpEvent.register('editReady', function () { 57 | if (!$scope.editMode && $scope.model.dbo_id != preview.dbo_id) { 58 | $scope.previewSocial() 59 | } else { 60 | updateDisplayMap() 61 | } 62 | }, $scope); 63 | 64 | updateDisplayMap(); 65 | $scope.toggleTarget(); 66 | 67 | }]); 68 | 69 | -------------------------------------------------------------------------------- /webclient/editor/view/mobile.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | 10 |
{{idOnly(model)}}
11 |
12 |
13 |
14 |
15 | 16 |
{{model.dbo_ts | dbDate}}
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 49 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
-------------------------------------------------------------------------------- /webclient/editor/panels/new_script.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Add User Script x 4 |
5 |
6 |
7 |
8 |
9 | 10 | 14 |
15 |
16 |
17 |
18 | 19 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 |
{{addObj.func_name}}
36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 | 46 |
47 | 50 | 52 |
53 |
54 |
55 | -------------------------------------------------------------------------------- /lampmud/mud/target.py: -------------------------------------------------------------------------------- 1 | from lampost.gameops.target import target_gen, recursive_targets, make_gen 2 | 3 | 4 | @target_gen 5 | def equip(key_type, target_key, entity, *_): 6 | return recursive_targets(key_type, [equip for equip in entity.inven if getattr(equip, 'current_slot', None)], target_key) 7 | equip.absent_msg = "You don't have `{target}' equipped." 8 | 9 | 10 | @target_gen 11 | def inven(key_type, target_key, entity, *_): 12 | return recursive_targets(key_type, [equip for equip in entity.inven if not getattr(equip, 'current_slot', None)], 13 | target_key) 14 | inven.absent_msg = "You don't have `{target}'." 15 | 16 | 17 | @target_gen 18 | def env(key_type, target_key, entity, *_): 19 | for extra in entity.env.extras: 20 | try: 21 | if target_key in getattr(extra.target_keys, key_type): 22 | yield extra 23 | except AttributeError: 24 | pass 25 | for target in recursive_targets(key_type, getattr(extra, 'target_providers', ()), target_key): 26 | yield target 27 | 28 | 29 | @target_gen 30 | def feature(key_type, target_key, entity, *_): 31 | return recursive_targets(key_type, (feature for feature in entity.env.features), target_key) 32 | 33 | 34 | @target_gen 35 | def env_living(key_type, target_key, entity, *_): 36 | return recursive_targets(key_type, (living for living in entity.env.denizens), target_key) 37 | 38 | 39 | @target_gen 40 | def player_env(key_type, target_key, entity, *_): 41 | return recursive_targets(key_type, (denizen for denizen in entity.env.denizens if getattr(denizen, 'is_player', False) and denizen != entity), target_key) 42 | 43 | 44 | @target_gen 45 | def env_items(key_type, target_key, entity, *_): 46 | return recursive_targets(key_type, [item for item in entity.env.inven], target_key) 47 | 48 | 49 | @target_gen 50 | def env_default(key_type, target_key, entity, *_): 51 | if not target_key: 52 | yield entity.env 53 | 54 | 55 | @target_gen 56 | def self_default(key_type, target_key, entity, *_): 57 | if not target_key: 58 | yield entity 59 | 60 | 61 | make_gen('self feature env_living env_items equip inven env_default', 'default') 62 | 63 | make_gen('self env_living self_default', 'living') 64 | 65 | make_gen('feature env_living env_items inven equip', '__invalid__') 66 | -------------------------------------------------------------------------------- /webclient/mud/js/character.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_mud').controller('NewCharacterCtrl', ['$scope', 'lpData', 'lpRemote', 'lpEvent', 'lpDialog', 2 | function ($scope, lpData, lpRemote, lpEvent, lpDialog) { 3 | 4 | $scope.playerName = ''; 5 | $scope.errorText = null; 6 | $scope.activeScreen = 'race'; 7 | $scope.playerData = {}; 8 | 9 | lpRemote.request("new_char_data").then(function (newCharData) { 10 | $scope.races = newCharData.races; 11 | $scope.ready = true; 12 | }); 13 | 14 | $scope.tryCancel = function () { 15 | if (lpData.playerIds.length == 0) { 16 | lpDialog.showConfirm("No Characters", "This account has no characters and will be deleted. Do you still wish to continue?").then( 17 | function () { 18 | lpRemote.request("settings/delete_account").then(function (response) { 19 | lpEvent.dispatchMap(response); 20 | }); 21 | $scope.dismiss(); 22 | }); 23 | } else { 24 | $scope.dismiss(); 25 | } 26 | }; 27 | 28 | $scope.createCharacter = function () { 29 | if ($scope.playerName.indexOf(" ") > -1) { 30 | $scope.errorText = "Spaces not permitted in character names"; 31 | return; 32 | } 33 | lpRemote.request("settings/create_player", {user_id: lpData.userId, player_name: $scope.playerName, player_data: $scope.playerData}) 34 | .then(function () { 35 | if (!lpData.playerId) { 36 | lpRemote.send('player_login', {player_id: $scope.playerName.toLowerCase()}); 37 | } 38 | lpEvent.dispatch('players_updated'); 39 | $scope.dismiss(); 40 | }, function (error) { 41 | $scope.errorText = error.text; 42 | }); 43 | }; 44 | 45 | }]); 46 | 47 | angular.module('lampost_mud').controller('SelectCharacterCtrl', ['$scope', 'lpRemote', 'lpEvent', 'lpData', 48 | function ($scope, lpRemote, lpEvent, lpData) { 49 | 50 | $scope.players = []; 51 | 52 | $scope.selectCharacter = function (playerId) { 53 | lpRemote.send('player_login', {player_id: playerId}); 54 | }; 55 | 56 | lpEvent.register('login', $scope.dismiss, $scope); 57 | 58 | loadCharacters(); 59 | function loadCharacters() { 60 | lpRemote.request("settings/get_players", {user_id: lpData.userId}).then(function (players) { 61 | $scope.players = players; 62 | }); 63 | } 64 | }]); 65 | -------------------------------------------------------------------------------- /webclient/editor/view/defense.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | 10 |
{{model.dbo_id}}
11 |
12 | 13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |
21 |
22 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 |
-------------------------------------------------------------------------------- /lampmud/model/player.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.event.zone import Attachable 3 | from lampost.gameops.target import TargetKeys 4 | from lampost.meta.auto import AutoField 5 | from lampost.db.dbo import KeyDBO, SystemDBO 6 | from lampost.db.dbofield import DBOField 7 | 8 | log = Injected('log') 9 | ev = Injected('dispatcher') 10 | module_inject(__name__) 11 | 12 | 13 | class Player(KeyDBO, SystemDBO, Attachable): 14 | dbo_key_type = "player" 15 | dbo_set_key = "players" 16 | 17 | session = AutoField() 18 | 19 | user_id = DBOField(0) 20 | created = DBOField(0) 21 | imm_level = DBOField(0) 22 | last_login = DBOField(0) 23 | last_logout = DBOField(0) 24 | age = DBOField(0) 25 | room_id = DBOField() 26 | home_room = DBOField() 27 | instance_id = DBOField(0) 28 | instance_room_id = DBOField() 29 | group = AutoField() 30 | 31 | is_player = True 32 | can_die = True 33 | 34 | @property 35 | def edit_dto(self): 36 | dto = super().edit_dto 37 | dto['logged_in'] = "Yes" if self.session else "No" 38 | return dto 39 | 40 | @property 41 | def name(self): 42 | return self.dbo_id.capitalize() 43 | 44 | @property 45 | def location(self): 46 | try: 47 | return getattr(self.env, "title") 48 | except AttributeError: 49 | return "Unknown" 50 | 51 | def _on_loaded(self): 52 | if not self.desc: 53 | self.desc = "An unimaginably powerful immortal." if self.imm_level else "A raceless, classless, sexless player." 54 | 55 | def _on_attach(self): 56 | ev.register_p(self.autosave, seconds=20) 57 | self.active_channels = set() 58 | self.target_keys = TargetKeys(self.dbo_id) 59 | self.last_tell = None 60 | 61 | def _on_detach(self): 62 | self.session = None 63 | 64 | def check_logout(self): 65 | pass 66 | 67 | def glance(self, source, **_): 68 | source.display_line("{0}, {1}".format(self.name, self.title or "An Immortal" if self.imm_level else "A Player")) 69 | 70 | def display_line(self, text, display='default'): 71 | if text and self.session: 72 | self.session.display_line({'text': text, 'display': display}) 73 | 74 | def output(self, output): 75 | if self.session: 76 | self.session.append(output) 77 | 78 | def receive_broadcast(self, broadcast): 79 | self.display_line(broadcast.translate(self), broadcast.display) 80 | 81 | def die(self): 82 | pass 83 | 84 | 85 | -------------------------------------------------------------------------------- /webclient/editor/panels/room_reset.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{addLabel}} Reset x 4 |
5 |
6 |
7 |
8 |
9 | No valid {{addLabel}}s are available. 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | 28 |
29 |
30 |
31 |
32 |
33 |
{{resetObj.title}}
34 |
{{resetObj.desc}}
35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 |
48 | 49 | 50 | 51 |
52 | 55 | 57 | 59 |
60 |
61 | 62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /webclient/mud/js/storage.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_mud').service('lpStorage', ['$window', 'lpEvent', function ($window, lpEvent) { 2 | 3 | var playerId; 4 | var sessionId; 5 | var sessions; 6 | var sessionKey = 'playerSessions'; 7 | var lastKey = 'lastPlayer'; 8 | var immKey = 'activeImm'; 9 | var immSession; 10 | 11 | function readSessions() { 12 | var value = localStorage.getItem(sessionKey); 13 | sessions = value ? JSON.parse(value) : {}; 14 | } 15 | 16 | function writeSessions() { 17 | localStorage.setItem(sessionKey, JSON.stringify(sessions)); 18 | if (playerId) { 19 | sessionStorage.setItem(lastKey, JSON.stringify({playerId: playerId, sessionId: sessionId, 20 | timestamp: new Date().getTime()})); 21 | } else { 22 | sessionStorage.removeItem(lastKey); 23 | } 24 | } 25 | 26 | function onLogout() { 27 | readSessions(); 28 | if (sessions.hasOwnProperty(playerId)) { 29 | delete sessions[playerId]; 30 | } 31 | playerId = null; 32 | writeSessions(); 33 | if (immSession) { 34 | localStorage.removeItem(immKey); 35 | immSession = null; 36 | } 37 | } 38 | 39 | function updateTimestamp() { 40 | readSessions(); 41 | if (playerId) { 42 | sessions[playerId] = {playerId: playerId, sessionId: sessionId, timestamp: new Date().getTime()}; 43 | } 44 | writeSessions(); 45 | } 46 | 47 | this.lastSession = function () { 48 | readSessions(); 49 | var staleTimestamp = new Date().getTime() - 60 * 1000; 50 | var lastSession; 51 | for (playerId in sessions) { 52 | if (sessions.hasOwnProperty(playerId)) { 53 | var session = sessions[playerId]; 54 | if (session.timestamp < staleTimestamp) { 55 | delete sessions[playerId]; 56 | } else if (!lastSession || session.timestamp > lastSession.timestamp) { 57 | lastSession = session; 58 | } 59 | } 60 | } 61 | var lastValue = sessionStorage.getItem(lastKey); 62 | if (lastValue) { 63 | lastSession = JSON.parse(lastValue); 64 | } 65 | writeSessions(); 66 | return lastSession; 67 | }; 68 | 69 | lpEvent.register('connect', function (data) { 70 | sessionId = data; 71 | }); 72 | 73 | lpEvent.register("heartbeat", updateTimestamp); 74 | 75 | lpEvent.register("login", function (data) { 76 | playerId = data.name.toLowerCase(); 77 | if (data.imm_level) { 78 | immSession = {user_id: data.user_id, app_session_id: sessionId}; 79 | localStorage.setItem(immKey, JSON.stringify(immSession)); 80 | } 81 | updateTimestamp(); 82 | }); 83 | 84 | lpEvent.register("logout", onLogout); 85 | 86 | lpEvent.register("window_closing", function () { 87 | if (playerId) { 88 | updateTimestamp(); 89 | } 90 | }); 91 | 92 | }]); 93 | -------------------------------------------------------------------------------- /lampmud/lpmud/mobile.py: -------------------------------------------------------------------------------- 1 | from lampost.di.app import on_app_start 2 | from lampost.di.config import on_config_change, config_value 3 | from lampost.di.resource import Injected, module_inject 4 | from lampost.meta.auto import TemplateField 5 | from lampost.db.dbofield import DBOField, DBOTField 6 | 7 | from lampmud.lpmud.attributes import fill_pools 8 | from lampmud.lpmud.entity import EntityLP, Skilled 9 | from lampmud.lpmud.skill import add_skill 10 | from lampmud.model.mobile import MobileTemplate, Mobile 11 | 12 | 13 | log = Injected('log') 14 | db = Injected('datastore') 15 | module_inject(__name__) 16 | 17 | 18 | @on_app_start 19 | @on_config_change 20 | def _config(): 21 | global affinities 22 | global attributes 23 | global base_attr_value 24 | affinities = config_value('affinities') 25 | attributes = config_value('attributes') 26 | base_attr_value = config_value('base_attr_value') 27 | 28 | 29 | class MobileTemplateLP(MobileTemplate): 30 | class_id = 'mobile' 31 | 32 | def _on_loaded(self): 33 | if self.archetype: 34 | arch = db.load_object(self.archetype, 'archetype') 35 | for attr_name, start_value in arch.base_attrs.items(): 36 | setattr(self.instance_cls, attr_name, start_value) 37 | self.desc = arch.desc 38 | else: 39 | for attr in attributes: 40 | setattr(self.instance_cls, attr['dbo_id'], base_attr_value * self.level) 41 | self.enemies = affinities[self.affinity]['enemies'] 42 | 43 | 44 | class MobileLP(EntityLP, Mobile, Skilled): 45 | template_id = "mobile" 46 | 47 | default_skills = DBOTField([], 'default_skill') 48 | archetype = DBOTField() 49 | level = DBOTField(1) 50 | affinity = DBOTField('neutral') 51 | enemies = TemplateField('enemies') 52 | guard_msg = DBOField("{source} stops you from moving {exit}.") 53 | 54 | def _on_attach(self): 55 | self.skills = {} 56 | for default_skill in self.default_skills: 57 | add_skill(default_skill.skill_template, self, default_skill.skill_level, 'mobile') 58 | fill_pools(self) 59 | 60 | def _on_detach(self): 61 | self.original_env.mobiles[self.template].remove(self) 62 | 63 | def entity_enter_env(self, entity, *_): 64 | self._react_entity(entity) 65 | 66 | def entity_leave_env(self, entity, exit_action): 67 | super().entity_leave_env(entity, exit_action) 68 | self.fight.check_follow(entity, exit_action) 69 | 70 | def enter_env(self, new_env, ex=None): 71 | super().enter_env(new_env, ex) 72 | for entity in new_env.denizens: 73 | self._react_entity(entity) 74 | 75 | def _react_entity(self, entity): 76 | if entity in self.fight.opponents.keys(): 77 | self.fight.add(entity) 78 | self.check_fight() 79 | elif hasattr(entity, 'affinity') and entity.affinity in self.enemies: 80 | self.start_combat(entity) 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /lampmud/editor/start.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import itertools 3 | from importlib import import_module 4 | 5 | from lampost.di.config import config_value 6 | from lampost.editor.editor import ChildrenEditor, Editor 7 | from lampost.editor.players import PlayerEditor, UserEditor 8 | from lampost.gameops.parser import action_keywords 9 | from lampost.gameops.script import Scriptable, Shadow, builders 10 | from lampost.db.registry import get_dbo_class, dbo_types, implementors, instance_implementors 11 | from lampost.server.link import link_route 12 | 13 | 14 | from lampmud.editor.areas import AreaEditor, RoomEditor 15 | from lampmud.editor.scripts import ScriptEditor 16 | from lampmud.editor.shared import SocialsEditor, SkillEditor 17 | from lampmud.env.movement import Direction 18 | from lampmud.comm.broadcast import broadcast_types, broadcast_tokens 19 | from lampmud.lpmud.skill import SkillTemplate 20 | 21 | import_module('lampost.editor.admin') 22 | import_module('lampost.editor.session') 23 | import_module('lampost.editor.dbops') 24 | 25 | AreaEditor() 26 | RoomEditor() 27 | ChildrenEditor('mobile') 28 | ChildrenEditor('article') 29 | PlayerEditor() 30 | UserEditor() 31 | SocialsEditor() 32 | Editor('race', create_level='founder') 33 | SkillEditor('attack') 34 | SkillEditor('defense') 35 | ScriptEditor() 36 | 37 | 38 | @link_route('editor/constants', 'creator') 39 | def editor_constants(**_): 40 | constants = {key: config_value(key) for key in ['attributes', 'resource_pools', 'equip_types', 'equip_slots', 'weapon_types', 41 | 'damage_types', 'damage_delivery', 'damage_groups', 'affinities', 'imm_levels']} 42 | constants['weapon_options'] = constants['weapon_types'] + [{'dbo_id': 'unused'}, {'dbo_id': 'unarmed'}, {'dbo_id': 'any'}] 43 | constants['skill_calculation'] = constants['attributes'] + [{'dbo_id': 'roll', 'name': 'Dice Roll'}, {'dbo_id': 'skill', 'name': 'Skill Level'}] 44 | constants['defense_damage_types'] = constants['damage_types'] + constants['damage_groups'] 45 | constants['directions'] = Direction.ordered 46 | constants['article_load_types'] = ['equip', 'default'] 47 | constants['broadcast_types'] = broadcast_types 48 | constants['broadcast_tokens'] = broadcast_tokens 49 | constants['skill_types'] = [skill_template.dbo_key_type for skill_template in dbo_types(SkillTemplate)] 50 | constants['features'] = [get_dbo_class(feature_id)().edit_dto for feature_id in ['touchstone', 'entrance', 'store']] 51 | constants['action_args'] = action_keywords 52 | shadow_types = {} 53 | for class_id, cls in itertools.chain(implementors(Scriptable), instance_implementors(Scriptable)): 54 | shadows = [{'name': name, 'args': inspect.getargspec(member.func).args} for name, member in inspect.getmembers(cls) if isinstance(member, Shadow)] 55 | if shadows: 56 | shadow_types[class_id] = shadows 57 | constants['shadow_types'] = shadow_types 58 | constants['script_builders'] = sorted((builder.dto for builder in builders), key=lambda dto: dto['name']) 59 | return(constants) 60 | -------------------------------------------------------------------------------- /webclient/mud/view/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |

Welcome to {{siteTitle}}

8 |
9 |
10 |
New Here? 11 |
12 | 13 |
14 |
15 |
16 |
17 |

18 |
19 |
20 |
21 | 22 | 25 | 26 |
27 |
28 | 29 | 31 | 32 | 33 |
34 |
35 | 36 | {{loginError}} 37 |
38 | 41 |
42 |
43 |
44 |
46 |
47 |
48 |
49 |
50 | 52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 | -------------------------------------------------------------------------------- /webclient/editor/panels/new_exit.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Dig New Exit x 4 |
5 | 6 |
7 |
8 |
9 |
10 | 11 | 13 |
14 |
15 |
16 | 20 | 24 | 28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 | 36 | 38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | 48 | 50 |
51 |
52 |
53 |
54 | 55 | 56 |
57 | 58 |
59 |
60 |
61 |
62 | 63 | {{lastError}} 64 |
65 |
66 |
67 | 68 | 69 |
70 |
71 | 72 | 73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /webclient/editor/view/social.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 |
{{model.dbo_id}}
8 |
9 |
10 | 11 |
12 | 15 | 18 |
19 |
20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 |
Adjust Preview Values
44 | 45 |
46 |
47 | Player 48 | 49 |
50 |
51 | Target 52 | 53 | 54 | 55 | 56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 | 70 | 72 |
73 | 74 |
75 |
76 |
77 | -------------------------------------------------------------------------------- /lampmud/setup/engine.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from importlib import import_module 4 | 5 | from tornado.ioloop import IOLoop 6 | from tornado.web import RedirectHandler, StaticFileHandler 7 | 8 | from lampost.util.logging import get_logger 9 | from lampost.di import resource, config, app 10 | from lampost.db.redisstore import RedisStore 11 | from lampost.db import dbconfig 12 | from lampost.util import json 13 | from lampost.db import permissions 14 | from lampost.event.system import dispatcher 15 | from lampost.gameops import friend 16 | from lampost.server import web 17 | from lampost.server.link import LinkHandler 18 | from lampost.server import pages 19 | from lampost.server import email as email_sender 20 | from lampost.server import display 21 | from lampost.server.services import AnyLoginService, PlayerListService, EditUpdateService 22 | from lampost.server import session as session_manager 23 | from lampost.server import user 24 | from lampost.server.channel import ChannelService 25 | from lampost.server import message 26 | 27 | from lampmud.env import instancemgr 28 | 29 | 30 | def start(args): 31 | json.select_json() 32 | 33 | datastore = resource.register('datastore', RedisStore(args.db_host, args.db_port, args.db_num, args.db_pw)) 34 | db_config = datastore.load_object(args.config_id, dbconfig.Config) 35 | config_values = config.activate(db_config.section_values) 36 | 37 | resource.register('dispatcher', dispatcher) 38 | resource.register('perm', permissions) 39 | 40 | app_setup = import_module('{}.appstart'.format(args.app_id)) 41 | app_setup.start_engine(args) 42 | 43 | resource.register('display', display) 44 | resource.register('user_manager', user) 45 | resource.register('session_manager', session_manager) 46 | resource.register('instance_manager', instancemgr) 47 | resource.register('email_sender', email_sender) 48 | resource.register('channel_service', ChannelService()) 49 | resource.register('friend_service', friend) 50 | resource.register('message_service', message) 51 | resource.register('player_list_service', PlayerListService()) 52 | resource.register('login_notify_service', AnyLoginService()) 53 | resource.register('edit_update_service', EditUpdateService()) 54 | 55 | app.start_app() 56 | 57 | pages.add_page(pages.LspPage('config.js', "var lampost_config = {{title:'{0}', description:'{1}'}};" 58 | .format(config_values['lampost_title'], config_values['lampost_description']))) 59 | 60 | tornado_logger = get_logger('tornado.general') 61 | tornado_logger.setLevel(config_values.get('tornado.log','WARN')) 62 | tornado_logger = get_logger('tornado.access') 63 | tornado_logger.setLevel(config_values.get('tornado.log', 'WARN')) 64 | web.service_root = args.service_root 65 | if args.web_files: 66 | web.add_raw_route("/", RedirectHandler, url="/webclient/lampost.html") 67 | web.add_raw_route("/webclient/(.*)", StaticFileHandler, path=os.path.abspath(args.web_files)) 68 | web.add_raw_route("/lsp/(.*)", pages.LspHandler) 69 | web.add_raw_route("/link", LinkHandler) 70 | web.start_service(args.port, args.server_interface) 71 | 72 | IOLoop.instance().start() 73 | -------------------------------------------------------------------------------- /webclient/mud/js/comm.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_mud').service('lmComm', ['lpEvent', 'lpData', 'lpRemote', 'lpDialog', function (lpEvent, lpData, lpRemote, lpDialog) { 2 | 3 | var self = this; 4 | var allLogins = false; 5 | lpEvent.register('friend_login', friendLogin); 6 | lpEvent.register('any_login', anyLogin); 7 | lpEvent.register('login', checkAllLogins); 8 | lpEvent.register('logout', function() { 9 | checkAllLogins(); 10 | }); 11 | lpEvent.register('notifies_updated', checkAllLogins); 12 | 13 | function checkAllLogins() { 14 | if (lpData.notifies.indexOf('allDesktop') > -1 || lpData.notifies.indexOf('allSound') > -1) { 15 | if (!allLogins) { 16 | lpRemote.registerService('login_notify_service'); 17 | allLogins = true; 18 | } 19 | } else { 20 | if (allLogins) { 21 | allLogins = false; 22 | lpRemote.unregisterService('login_notify_service'); 23 | 24 | } 25 | } 26 | } 27 | 28 | function friendLogin(friend_info) { 29 | if (lpData.notifies.indexOf('friendDesktop') > -1 && lpData.notifies.indexOf('allDesktop') == -1) { 30 | self.notify({icon: 'image/friendNotify.png', title: "Your friend " + friend_info.name + " logged into " + lampost_config.title, content: ''}); 31 | 32 | } 33 | if (lpData.notifies.indexOf('friendSound') > -1 && lpData.notifies.indexOf('allSound') == -1) { 34 | jQuery('#sound_beep_ping')[0].play(); 35 | 36 | } 37 | lpEvent.dispatch('display_line', 'Your friend ' + friend_info.name + ' just logged in', 'system'); 38 | } 39 | 40 | function anyLogin(login) { 41 | if (lpData.notifies.indexOf('allDesktop') > -1) { 42 | self.notify({icon: 'image/friendNotify.png', title: "Player " + login.name + " logged into " + lampost_config.title, content: ''}); 43 | } 44 | 45 | if (lpData.notifies.indexOf('allSound') > -1) { 46 | jQuery('#sound_beep_ping')[0].play(); 47 | lpEvent.dispatch('display_line', login.name + ' just logged in', 'system'); 48 | } 49 | 50 | } 51 | 52 | function showNotification(notify_data) { 53 | var notification = window.webkitNotifications.createNotification(notify_data.icon, notify_data.title, notify_data.content); 54 | notification.show(); 55 | } 56 | 57 | this.requestNotificationPermission = function (notify_data) { 58 | lpDialog.showConfirm("Allow Notifications", "You must grant permission to allow notifications from " + lampost_config.title).then(function () { 59 | window.webkitNotifications.requestPermission(function () { 60 | self.notify(notify_data); 61 | }) 62 | }); 63 | }; 64 | 65 | this.notify = function (notify_data) { 66 | if (!window.webkitNotifications) { 67 | return; 68 | } 69 | var permLevel = window.webkitNotifications.checkPermission(); 70 | if (permLevel == 0) { 71 | showNotification(notify_data); 72 | } else if (permLevel == 1) { 73 | self.requestNotificationPermission(notify_data); 74 | } 75 | } 76 | 77 | }]); 78 | -------------------------------------------------------------------------------- /webclient/editor/view/race.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | 10 |
{{model.dbo_id}}
11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 45 |
46 |
47 |
48 |
49 | 50 | 54 |
55 |
56 |
57 |
58 |
59 | 60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 |
68 | 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | 82 |
83 |
84 | -------------------------------------------------------------------------------- /webclient/mud/dialogs/new_account.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webclient/lampost.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lampost Mud 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 53 |
54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /webclient/mud/dialogs/new_character.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webclient/editor/view/config.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 |
8 |
9 |
10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 23 | 24 | 26 | 27 | 28 |
29 | 30 |
31 |
32 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 45 | 46 | 47 |
{{name}} 44 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 60 | 61 | 62 |
{{name}} 57 | 59 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | -------------------------------------------------------------------------------- /webclient/editor/view/attack.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | 10 |
{{model.dbo_id}}
11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 | 30 | 31 |
32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 | 55 |
56 |
57 | 58 | 60 |
61 | 62 |
63 | 64 | 66 |
67 | 68 |
69 | 70 | 72 |
73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 | 82 |
83 | -------------------------------------------------------------------------------- /webclient/editor/view/player.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 |
{{model.dbo_id | cap}}
10 |
11 |
12 |
13 |
14 | 15 | 16 |
{{model.user_id}}
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 | 27 |
28 |
29 |
30 |
31 | 32 | 34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 |
{{model.last_login * 1000 | date: 'short'}} 50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 |
{{model.last_logout * 1000 | date: 'short'}} 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 68 | 70 |
71 |
72 |
73 |
74 | 75 | 79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 | -------------------------------------------------------------------------------- /webclient/editor/panels/entrance.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{newAdd ? 'Create' : 'Modify'}} Entrance x 5 |
6 |
7 | 8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 |
16 |
17 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 | 41 | 43 |
44 |
45 | 46 |
47 |
48 |
49 | 50 | 52 |
53 |
54 | 55 |
56 |
57 | 58 | 60 |
61 |
62 |
63 | 64 |
65 |
66 |
67 | {{destRoom.title}} 68 |

69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 | 77 | {{lastError}} 78 |
79 |
80 |
81 | 82 |
83 |
84 |
85 | 86 | 87 |
88 |
89 | 90 | 91 |
92 |
93 |
94 |
95 |
96 | -------------------------------------------------------------------------------- /lampmud/lpmud/player.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.db.dbofield import DBOField, DBOLField 3 | from lampost.gameops.action import ActionError 4 | from lampost.di.config import config_value 5 | 6 | from lampmud.lpmud.attributes import base_pools, fill_pools 7 | from lampmud.lpmud.entity import EntityLP, Skilled 8 | from lampmud.lpmud.skill import add_skill 9 | from lampmud.model.item import ItemDBO 10 | 11 | from lampmud.model.player import Player 12 | 13 | ev = Injected('dispatcher') 14 | db = Injected('datastore') 15 | module_inject(__name__) 16 | 17 | 18 | class PlayerLP(Player, EntityLP, ItemDBO, Skilled): 19 | dbo_key_type = 'player' 20 | 21 | race = DBOLField(dbo_class_id='race') 22 | touchstone = DBOField() 23 | size = DBOField('medium') 24 | affinity = 'player' 25 | can_die = True 26 | 27 | _bind_pulse = None 28 | 29 | def _on_db_created(self): 30 | for attr_name, start_value in self.race.base_attrs.items(): 31 | setattr(self, attr_name, start_value) 32 | setattr(self, 'perm_{}'.format(attr_name), start_value) 33 | fill_pools(self) 34 | if self.race.start_instanced: 35 | self.instance_room_id = self.race.start_room.dbo_id 36 | self.room_id = None 37 | else: 38 | self.room_id = self.race.start_room.dbo_id 39 | 40 | def _on_attach(self): 41 | self.auto_fight = False 42 | for skill in self.skills.values(): 43 | self._apply_skill(skill) 44 | if self.race: 45 | for default_skill in self.race.default_skills: 46 | if default_skill.skill_template.dbo_key not in self.skills.keys(): 47 | add_skill(default_skill.skill_template, self, default_skill.skill_level, 'race') 48 | 49 | base_pools(self) 50 | self.check_status() 51 | 52 | def check_logout(self): 53 | if self.last_opponent: 54 | self.display_line("You are in life threatening combat! You can't log out right now.", 'combat') 55 | if self.imm_level: 56 | self.display_line("(You might want to consider imposing 'peace.')") 57 | raise ActionError() 58 | 59 | def status_change(self): 60 | if not self.session: 61 | return 62 | status = self.display_status 63 | if self.last_opponent and self.last_opponent.env == self.env: 64 | status['opponent'] = self.last_opponent.display_status 65 | status['opponent']['name'] = self.last_opponent.name 66 | else: 67 | status['opponent'] = None 68 | self.session.update_status(status) 69 | 70 | def die(self): 71 | if self.can_die: 72 | self.broadcast(s="Alas, you succumb to your injuries.", e="{n} dies.", display="combat") 73 | self.display_line("Unless other help intercedes, you will be returned to your last touchstone in 90 seconds.
" 74 | "You may hasten the end if you 'abandon' all hope of assistance.", 'system') 75 | self._death_effects() 76 | self._bind_pulse = ev.register_once(self.resurrect, seconds=90) 77 | else: 78 | self.broadcast(s="You die. Fortunately, you're immortal.", e="{n} examines {s} otherwise fatal wounds and shrugs.") 79 | self.health = 1 80 | self.check_status() 81 | 82 | def resurrect(self, auto=True): 83 | ev.unregister(self._bind_pulse) 84 | del self._bind_pulse 85 | res_room = None 86 | if self.touchstone: 87 | res_room = db.load_object(self.touchstone, 'room') 88 | if not res_room: 89 | res_room = db.load_object(config_value('default_start_room'), 'room') 90 | self.change_env(res_room) 91 | self.display_line("With a sick feeling, you return to consciousness") 92 | self.status = 'ok' 93 | self.health = 1 94 | self.check_status() 95 | -------------------------------------------------------------------------------- /webclient/editor/js/feature_editors.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').controller('EditStoreCtrl', ['$scope', '$filter', function($scope, $filter) { 2 | 3 | var noCurrency = {dbo_id: ':--None--'}; 4 | var noItems = {dbo_id: ':--No Items--'}; 5 | 6 | $scope.store = $scope.activeFeature; 7 | $scope.currencyParent = $scope.store.currency ? $scope.store.currency.split(":")[0] : undefined; 8 | $scope.newPerm = noItems.dbo_id; 9 | 10 | $scope.setCurrencyList = function(objects) { 11 | $scope.currencyList = $filter('filter')(objects, {divisible: true}); 12 | $scope.currencyList.unshift(noCurrency); 13 | 14 | for (var ix = 0; ix < $scope.currencyList.length; ix++) { 15 | if ($scope.store.currency == $scope.currencyList[ix].dbo_id) { 16 | $scope.newCurrency = $scope.store.currency; 17 | return; 18 | } 19 | } 20 | $scope.newCurrency = noCurrency.dbo_id; 21 | $scope.store.currency = null; 22 | }; 23 | 24 | 25 | $scope.setPermList = function (objects) { 26 | $scope.permList = $filter('filter')(objects, {divisible: false}); 27 | if ($scope.permList.length === 0) { 28 | $scope.permList = [noItems]; 29 | } 30 | $scope.newPerm = $scope.permList[0].dbo_id; 31 | }; 32 | 33 | $scope.updateCurrency = function() { 34 | if ($scope.newCurrency == noCurrency.dbo_id) { 35 | $scope.store.currency = null; 36 | } else { 37 | $scope.store.currency = $scope.newCurrency; 38 | } 39 | }; 40 | 41 | $scope.addPerm = function() { 42 | $scope.store.perm_inven.push($scope.newPerm); 43 | }; 44 | 45 | $scope.permExists = function() { 46 | return $scope.newPerm === noItems.dbo_id || $scope.store.perm_inven.indexOf($scope.newPerm) > -1; 47 | }; 48 | 49 | $scope.removePerm = function(perm) { 50 | var ix = $scope.store.perm_inven.indexOf(perm); 51 | if (ix > -1) { 52 | $scope.store.perm_inven.splice(ix, 1); 53 | } 54 | }; 55 | 56 | }]); 57 | 58 | 59 | angular.module('lampost_editor').controller('EditEntranceCtrl', ['$scope', 'lpEditFilters', 'lpEditor', 60 | function($scope, lpEditFilters, lpEditor) { 61 | 62 | $scope.ent_dirs = angular.copy(lpEditor.constants.directions); 63 | $scope.ent_dirs.unshift({key: 'unused', name: "Use Command"}); 64 | $scope.parentFilter = lpEditFilters.hasChild('room'); 65 | 66 | function initialize() { 67 | $scope.entrance = angular.copy($scope.activeFeature); 68 | $scope.newAdd = !!lpEditor.addObj; 69 | if (!$scope.entrance.direction) { 70 | $scope.entrance.direction = 'unused'; 71 | } 72 | } 73 | 74 | $scope.listChange = function(children) { 75 | $scope.childList = children; 76 | $scope.entrance.destination = children[0].dbo_id; 77 | $scope.destRoom = children[0]; 78 | }; 79 | 80 | $scope.checkVerb = function() { 81 | if ($scope.entrance.verb) { 82 | $scope.entrance.direction = 'unused'; 83 | } 84 | }; 85 | 86 | $scope.checkDirection = function () { 87 | if ($scope.entrance.direction != 'unused') { 88 | $scope.entrance.verb = null; 89 | } 90 | }; 91 | 92 | $scope.updateRoom = function() { 93 | $scope.entrance.destination = $scope.destRoom.dbo_id; 94 | }; 95 | 96 | $scope.updateEntrance = function() { 97 | if (!$scope.entrance.title && !$scope.entrance.desc) { 98 | $scope.lastError = "Title and description required."; 99 | return; 100 | } 101 | if ($scope.entrance.direction == 'unused') { 102 | $scope.entrance.direction = null; 103 | } 104 | if ($scope.newAdd) { 105 | $scope.model.features.push($scope.entrance); 106 | } else { 107 | angular.copy($scope.entrance, $scope.activeFeature); 108 | } 109 | $scope.closeAdd(); 110 | }; 111 | 112 | $scope.$on('addInit', initialize); 113 | 114 | initialize(); 115 | 116 | 117 | 118 | }]); 119 | -------------------------------------------------------------------------------- /webclient/editor/js/skills_editor.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_editor').service('lpSkillService', [ 'lpCache', 'lpEditor', 2 | function(lpCache, lpEditor) { 3 | 4 | this.allSkills = function() { 5 | var skillLists = []; 6 | angular.forEach(lpEditor.constants.skill_types, function(skillType) { 7 | skillLists.push(lpCache.cachedList(skillType)); 8 | }); 9 | return [].concat.apply([], skillLists); 10 | }; 11 | 12 | }]); 13 | 14 | angular.module('lampost_editor').controller('AttackEditorCtrl', ['$scope', 'lpEditor', 'lpEditorTypes', 15 | function ($scope, lpEditor, lpEditorTypes) { 16 | 17 | var damageList = new lpEditorTypes.ValueMap('damage_calc', 'Damage Calculation'); 18 | damageList.desc = 'Calculation of Damage based on attributes and roll'; 19 | damageList.options = lpEditor.constants.skill_calculation; 20 | damageList.size = 'sm'; 21 | $scope.damageList = damageList; 22 | 23 | var accuracyList = new lpEditorTypes.ValueMap('accuracy_calc', 'Accuracy Calculation'); 24 | accuracyList.desc = 'Calculation of Accuracy based on attributes and roll'; 25 | accuracyList.options = lpEditor.constants.skill_calculation; 26 | accuracyList.size = 'sm'; 27 | $scope.accuracyList = accuracyList; 28 | 29 | var costList = new lpEditorTypes.ValueMap('costs', 'Skill Costs'); 30 | costList.options = lpEditor.constants.resource_pools; 31 | costList.size = 'sm'; 32 | $scope.costList = costList; 33 | 34 | }]); 35 | 36 | 37 | angular.module('lampost_editor').controller('DefenseEditorCtrl', ['$scope', 'lpEditor', 'lpEditorTypes', 38 | function ($scope, lpEditor, lpEditorTypes) { 39 | 40 | var absorbList = new lpEditorTypes.ValueMap('absorb_calc', 'Absorb Calculation'); 41 | absorbList.desc = 'Calculation of absorb amount based on attributes and roll'; 42 | absorbList.options = lpEditor.constants.skill_calculation; 43 | absorbList.size = 'sm'; 44 | $scope.absorbList = absorbList; 45 | 46 | var avoidList = new lpEditorTypes.ValueMap('avoid_calc', 'Avoid Calculation'); 47 | avoidList.desc = 'Calculation of avoid chance based on attributes and roll'; 48 | avoidList.options = lpEditor.constants.skill_calculation; 49 | avoidList.size = 'sm'; 50 | $scope.avoidList = avoidList; 51 | 52 | var costList = new lpEditorTypes.ValueMap('costs', 'Skill Costs'); 53 | costList.options = lpEditor.constants.resource_pools; 54 | costList.size = 'sm'; 55 | $scope.costList = costList; 56 | 57 | $scope.damageTypes = new lpEditorTypes.OptionsList('damage_type', 'Damage Types'); 58 | $scope.damageTypes.desc = 'List of damage types this defense is effective against'; 59 | $scope.damageTypes.setOptions(lpEditor.constants.defense_damage_types); 60 | 61 | $scope.deliveryTypes = new lpEditorTypes.OptionsList('delivery', 'Delivery Methods'); 62 | $scope.deliveryTypes.desc = 'List of delivery methods this defense is effective against'; 63 | $scope.deliveryTypes.setOptions(lpEditor.constants.damage_delivery); 64 | 65 | $scope.onAutoStart = function () { 66 | if ($scope.model.auto_start) { 67 | $scope.model.verb = undefined; 68 | } 69 | }; 70 | 71 | }]); 72 | 73 | 74 | angular.module('lampost_editor').controller('RaceEditorCtrl', ['$scope', 'lpEditor', 'lpEditorTypes', 'lpSkillService', 75 | function ($scope, lpEditor, lpEditorTypes, lpSkillService) { 76 | 77 | var attr_map = {}; 78 | 79 | angular.forEach(lpEditor.constants.attributes, function(attr) { 80 | attr_map[attr.dbo_id] = attr; 81 | }); 82 | 83 | var attrSet = new lpEditorTypes.ValueMap('base_attrs', 'Starting Attributes'); 84 | attrSet.rowLabel = function(row) { 85 | return attr_map[row.key].name; 86 | }; 87 | 88 | $scope.attrSet = attrSet; 89 | 90 | var skillSet = new lpEditorTypes.ValueObjList('default_skills', "Default Skills [Level]", 'skill_template', 'skill_level'); 91 | skillSet.options = lpSkillService.allSkills(); 92 | skillSet.optionKey = 'dbo_key'; 93 | $scope.skillSet = skillSet; 94 | 95 | $scope.startRoomSelect = new lpEditorTypes.ChildSelect('start_room', 'room'); 96 | 97 | }]); 98 | -------------------------------------------------------------------------------- /webclient/common/js/directives.js: -------------------------------------------------------------------------------- 1 | angular.module('lampost_dir', []); 2 | 3 | angular.module('lampost_dir').directive("enterKey", function () { 4 | return { 5 | restrict:'A', 6 | link:function (scope, element, attrs) { 7 | element.bind('keypress', function (event) { 8 | if (event.keyCode == 13) { 9 | event.preventDefault(); 10 | scope.$apply(scope.$eval(attrs.enterKey)); 11 | return false; 12 | } 13 | return true; 14 | }) 15 | } 16 | } 17 | }); 18 | 19 | angular.module('lampost_dir').directive('autoComplete', function () { 20 | return { 21 | restrict:'A', 22 | require:'ngModel', 23 | link:function (scope, element, attrs, ngModel) { 24 | var opts = {}; 25 | opts.source = scope.$eval(attrs.autoComplete); 26 | opts.updater = function (item) { 27 | ngModel.$setViewValue(item); 28 | }; 29 | element.typeahead(opts); 30 | } 31 | } 32 | }); 33 | 34 | 35 | angular.module('lampost_dir').directive('scrollBottom', ['$timeout', function ($timeout) { 36 | return { 37 | restrict:'A', 38 | link:function (scope, element, attrs) { 39 | scope.$watch(attrs.scrollBottom, function () { 40 | $timeout(function () { 41 | var scrollHeight = element[0].scrollHeight; 42 | if (scrollHeight) { 43 | element.scrollTop(scrollHeight); 44 | } 45 | }); 46 | }) 47 | } 48 | }; 49 | }]); 50 | 51 | angular.module('lampost_dir').directive('history', function () { 52 | return { 53 | restrict:'A', 54 | link:function (scope, element) { 55 | element.bind('keydown', function (event) { 56 | var apply = null; 57 | if (event.keyCode == 38) { 58 | apply = function () { 59 | scope.historyUp() 60 | }; 61 | } else if (event.keyCode == 40) { 62 | apply = function () { 63 | scope.historyDown() 64 | }; 65 | } 66 | if (apply) { 67 | scope.$apply(apply); 68 | event.preventDefault(); 69 | return false; 70 | } 71 | return true; 72 | }); 73 | } 74 | } 75 | }); 76 | 77 | angular.module('lampost_dir').directive("prefFocus", ['$rootScope', '$timeout', function ($rootScope, $timeout) { 78 | return { 79 | restrict:"A", 80 | link:function (scope, element) { 81 | scope.$on("refocus", forceFocus); 82 | var timer = $timeout(forceFocus); 83 | 84 | function forceFocus() { 85 | $timeout.cancel(timer); 86 | $(element)[0].focus(); 87 | } 88 | } 89 | } 90 | }]); 91 | 92 | angular.module('lampost_dir').directive("lmStep", [function() { 93 | return { 94 | restrict: "A", 95 | link: function(scope, element, attrs) { 96 | element.attr("step", scope.$eval(attrs.lmStep)); 97 | } 98 | } 99 | }]); 100 | 101 | 102 | angular.module('lampost_dir').directive("colorPicker", [function () { 103 | return { 104 | restrict: "A", 105 | scope: {color: '=ngModel'}, 106 | link: function (scope, element) { 107 | element.spectrum({ 108 | color: scope.color, 109 | change: function(color) { 110 | scope.$apply(function() { 111 | scope.color = color.toHexString(true); 112 | }); 113 | }, 114 | showInitial: true, 115 | showInput: true, 116 | preferredFormat: 'hex' 117 | }); 118 | scope.$watch('color', function() { 119 | element.spectrum('set', scope.color); 120 | }); 121 | } 122 | } 123 | 124 | }]); 125 | -------------------------------------------------------------------------------- /lampmud/env/instance.py: -------------------------------------------------------------------------------- 1 | from lampost.db.dbofield import DBOField, DBOLField 2 | 3 | from lampost.di.resource import Injected, module_inject 4 | from lampost.gameops import target 5 | from lampost.gameops.action import ActionError 6 | 7 | from lampmud.comm.broadcast import BroadcastMap 8 | from lampmud.env.movement import Direction 9 | from lampmud.model.item import ItemDBO 10 | 11 | log = Injected('log') 12 | ev = Injected('dispatcher') 13 | instance_manager = Injected('instance_manager') 14 | module_inject(__name__) 15 | 16 | 17 | class AreaInstance(): 18 | def __init__(self, instance_id): 19 | self.instance_id = instance_id 20 | self.entities = set() 21 | self.rooms = {} 22 | self.pulse_stamp = ev.current_pulse 23 | 24 | def add_entity(self, entity): 25 | self.entities.add(entity) 26 | entity.instance = self 27 | self.pulse_stamp = ev.current_pulse 28 | 29 | def remove_entity(self, entity): 30 | if entity in self.entities: 31 | del entity.instance 32 | self.entities.remove(entity) 33 | if not self.entities: 34 | self.clear_rooms() 35 | if entity.group: 36 | entity.group.instance = None 37 | instance_manager.delete(self.instance_id) 38 | 39 | def clear_rooms(self): 40 | for room in self.rooms.values(): 41 | room.detach() 42 | 43 | def get_room(self, room): 44 | if not room: 45 | log.error("Null room passed to area instance") 46 | return 47 | try: 48 | my_room = self.rooms[room.dbo_id] 49 | except KeyError: 50 | my_room = room.clone() 51 | my_room.instance = self 52 | self.rooms[room.dbo_id] = my_room 53 | return my_room 54 | 55 | 56 | verb_exit = BroadcastMap(ea='{n} leaves {N}') 57 | dir_exit = BroadcastMap(ea='{n} leaves to the {N}') 58 | verb_entry = BroadcastMap(ea='{n} arrives {N}') 59 | dir_enter = BroadcastMap(ea='{n} arrives from the {N}') 60 | 61 | 62 | class Entrance(ItemDBO): 63 | class_id = 'entrance' 64 | 65 | destination = DBOLField(dbo_class_id="room", required=True) 66 | verb = DBOField('enter') 67 | direction = DBOField(None) 68 | instanced = DBOField(True) 69 | edit_required = DBOField(True) 70 | 71 | match_args = 'source', 72 | 73 | def _on_loaded(self): 74 | if self.direction: 75 | self._dir = Direction.ref_map[self.direction] 76 | self.verbs = self._dir.obj_id, self._dir.desc 77 | self.target_class = 'no_args' 78 | else: 79 | self._dir = None 80 | self.verbs = self.verb 81 | self.target_class = target.make_gen('action') 82 | if not self.title and self.verb: 83 | self.title = self.verb 84 | 85 | @property 86 | def from_name(self): 87 | return Direction.ref_map.get(self._dir.rev_key).desc if self.direction else self.title 88 | 89 | @property 90 | def entry_msg(self): 91 | return verb_entry if self.verb else verb_entry 92 | 93 | @property 94 | def exit_msg(self): 95 | return verb_exit if self.verb else dir_exit 96 | 97 | def glance(self, source): 98 | if self._dir: 99 | source.display_line('Exit: {0} {1}'.format(self._dir.desc, self.destination.title), 'exit') 100 | else: 101 | source.display_line(self.title, 'exit') 102 | 103 | def __call__(self, source): 104 | if self.instanced: 105 | if getattr(source, 'group', None): 106 | if source.group.instance: 107 | instance = source.group.instance 108 | if self.destination not in instance.rooms: 109 | raise ActionError("Your group has entered a different instance. You must leave your group to go this way.") 110 | else: 111 | instance = source.group.instance = instance_manager.next_instance() 112 | else: 113 | instance = instance_manager.next_instance() 114 | destination = instance.get_room(self.destination) 115 | else: 116 | destination = self.destination 117 | source.change_env(destination, self) 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Development Suspended 2 | 3 | This project is no longer updated. The active version of Lampost is now on Gitlab: 4 | 5 | [lampost_lib](https://gitlab.com/avezel/lampost_lib) 6 | [lampost_mud](https://gitlab.com/avezel/lampost_mud) 7 | [lampost_ui](https://gitlab.com/avezel/lampost-ui) 8 | 9 | These "current" versions do not have full support for the angular.js client. Instead they use a React.js/mobx based client. 10 | My development efforts are now focused on building a non-MUD game using the Lampost framework, so there will not be a React.js MUD 11 | client/editor in the near future. Accordingly if you need a MUD UI you are limited to using these Github versions of the code, or 12 | of course, I would welcome someone else expanding the lampost_ui project to include full MUD support. 13 | 14 | As always, please contact me at with any questions or ideas. 15 | 16 | 17 | ## Lampost 18 | 19 | Lampost is a multi-user virtual world building platform inspired by the thriving community of multi-user dungeons (MUDs) of the 1990s. 20 | 21 | 22 | ### Quickstart 23 | 24 | * Install Python 3.4+ 25 | * Install redis-py 2.10.3+, Tornado 4.5+ and PyYAML 3.10+ (for configuration) using pip 26 | * Install Redis 2.4+ 27 | * Start Redis with the redis-server script 28 | * Clone this Github repository 29 | * Run lampost_setup --imm_name YOUR_SUPERUSER_PLAYER_NAME 30 | * Run lampost.py 31 | * Point your browser at 32 | 33 | ### Lampost Components 34 | 35 | #### Application Server 36 | 37 | The Lampost application server manages user/player sessions, client communication (via Web Sockets), and 38 | the game engine via asynchronous, event driven atomic operations. 39 | 40 | #### Web Client 41 | 42 | The Lampost web client is an [angular.js](https://angularjs.org) single page application. It provides a clean UI to the 43 | Lampost Game Engine, as well as underlying support for Lampost sessions and client services. 44 | 45 | #### Game Editor 46 | 47 | The Lampost editor is a full featured administrative web client, also built on angular.js. The editor allows building 48 | traditional MUD environments in minutes, with real time creation of items, mobiles, rooms, areas (both shared and instanced) 49 | and user scripts. 50 | 51 | 52 | ### Feature Highlights 53 | 54 | #### Seamless Persistence 55 | 56 | Lampost game objects are persisted in back end data storage. Adding, subclassing, and combining persistent objects is all 57 | but invisible to the developer. Say goodbye to table definitions, xml configuration, lifecycle management, and all of the 58 | other annoying overhead of most persistence frameworks. 59 | 60 | Lampost by default uses Redis, but support for other key/value stores requires modifying only a single Lampost class. 61 | 62 | #### Intelligent Parsing 63 | 64 | Lampost includes a sophisticated command parser designed around actions in a virtual world. The parser is context aware, 65 | tailoring available actions and targets to the player, their abilities, their possessions, and their environment. 66 | The context aware algorithm results in extremely fast parsing performance. 67 | 68 | #### User Scripts 69 | 70 | Lampost has experimental support for user supplied scripts, allowing users to dynamically attach Python code to in-game objects 71 | to dramatically expand the ability to customize the game experience without modifying source code. 72 | 73 | #### Websocket API 74 | 75 | Lampost has a full featured JSON/Websocket API for creating, modifying, and deleting in game objects. This API supports the 76 | Lampost editor. 77 | 78 | 79 | ### Requirements 80 | 81 | The Lampost application server requires Python 3.4 or later. Lampost has been run successfully on Linux, Windows, and OS X (CPython), 82 | and smoke tested on [Pypy3 2.4.0](http://pypy.org) (Ubuntu). 83 | 84 | The Lampost web server is built on the [Tornado](http://www.tornadoweb.org) web server. Lampost has been tested on Tornado 4.0.2. 85 | 86 | Lampost uses [Redis](http://redis.io) as its primary, high performance key/value datastore. Lampost is currently compatible with 87 | Redis 2.4 and later versions. Lampost requires the awesome [redis-py](https://github.com/andymccurdy/redis-py) library for 88 | Redis connectivity. 89 | 90 | Finally Lampost requires PyYAML 3.x for initial configuration. 91 | 92 | 93 | ### License 94 | 95 | Lampost is covered by the MIT license, a copy of which is included in this source tree. 96 | -------------------------------------------------------------------------------- /lampmud/model/article.py: -------------------------------------------------------------------------------- 1 | from lampost.gameops.script import Shadow 2 | from lampost.meta.auto import TemplateField 3 | from lampost.db.dbo import CoreDBO, ChildDBO 4 | from lampost.db.dbofield import DBOField, DBOTField 5 | from lampost.db.template import Template 6 | from lampost.gameops.action import ActionError 7 | from lampost.util.lputil import plural 8 | 9 | from lampmud.model.item import ItemInstance, target_keys 10 | 11 | VOWELS = {'a', 'e', 'i', 'o', 'u'} 12 | 13 | 14 | class ArticleTemplate(ChildDBO, Template): 15 | dbo_key_type = "article" 16 | dbo_parent_type = "area" 17 | 18 | def plural_name(self, quantity): 19 | if quantity == 1: 20 | return self.title 21 | return self.plural_title 22 | 23 | def _on_loaded(self): 24 | self.single_keys = target_keys(self) 25 | 26 | if self.divisible: 27 | self.plural_title = plural(self.title) 28 | self.plural_keys = target_keys(self) 29 | self.plural_keys.add(self.plural_title) 30 | 31 | def config_instance(self, instance, owner): 32 | instance.attach() 33 | 34 | 35 | class Article(ItemInstance): 36 | template_id = 'article' 37 | 38 | weight = DBOTField(0) 39 | value = DBOTField(0) 40 | divisible = DBOTField(False) 41 | art_type = DBOTField('treasure') 42 | level = DBOTField(1) 43 | quantity = DBOField() 44 | uses = DBOField() 45 | single_keys = TemplateField() 46 | plural_keys = TemplateField() 47 | plural_title = TemplateField() 48 | 49 | @property 50 | def name(self): 51 | if self.quantity and self.quantity > 1: 52 | prefix = str(self.quantity) 53 | title = self.plural_title 54 | elif self.title.lower().startswith(('a ', 'an ')): 55 | prefix = "" 56 | title = self.title 57 | else: 58 | prefix = "an" if self.title[0] in VOWELS else "a" 59 | title = self.title 60 | equipped = ' (equipped)' if self.current_slot else '' 61 | return "{} {}{}".format(prefix, title, equipped) 62 | 63 | @property 64 | def target_keys(self): 65 | if self.quantity and self.quantity > 1: 66 | return self.plural_keys 67 | return self.single_keys 68 | 69 | @Shadow 70 | def short_desc(self, observer=None): 71 | return self.name.capitalize() 72 | 73 | @Shadow 74 | def long_desc(self, observer=None): 75 | long_desc = super().long_desc(observer) 76 | if self.quantity: 77 | return "{} ({})".format(long_desc, self.quantity) 78 | return long_desc 79 | 80 | @Shadow 81 | def get(self, source, quantity=None): 82 | source.check_inven(self, quantity) 83 | gotten = self 84 | if quantity and quantity < self.quantity: 85 | gotten = self.template.create_instance() 86 | gotten.quantity = quantity 87 | self.quantity -= quantity 88 | else: 89 | source.env.remove_inven(self) 90 | source.broadcast(s="You pick up {N}", e="{n} picks up {N}", target=gotten) 91 | gotten.enter_env(source) 92 | 93 | @Shadow 94 | def drop(self, source, quantity=None): 95 | source.check_drop(self, quantity) 96 | drop = self.take_from(source, quantity) 97 | drop.enter_env(source.env) 98 | source.broadcast(s="You drop {N}", e="{n} drops {N}", target=drop) 99 | 100 | @Shadow 101 | def take_from(self, source, quantity): 102 | if quantity and quantity < self.quantity: 103 | self.quantity -= quantity 104 | drop = self.template.create_instance() 105 | drop.quantity = quantity 106 | else: 107 | drop = self 108 | source.remove_inven(self) 109 | return drop 110 | 111 | @Shadow 112 | def enter_env(self, new_env): 113 | if self.quantity: 114 | try: 115 | existing = [item for item in new_env.inven if item.template == self.template][0] 116 | existing.quantity += self.quantity 117 | return 118 | except IndexError: 119 | pass 120 | new_env.add_inven(self) 121 | 122 | 123 | class ArticleReset(CoreDBO): 124 | class_id = 'article_reset' 125 | article = DBOField(dbo_class_id='article', required=True) 126 | reset_count = DBOField(1) 127 | reset_max = DBOField(1) 128 | mobile_ref = DBOField(0) 129 | load_type = DBOField('equip') 130 | -------------------------------------------------------------------------------- /lampmud/comm/broadcast.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from lampost.util.lputil import pronouns 4 | 5 | defaults = {'e': 's', 't': 'e', 'st': 's', 'et': 'e', 'sf': 's', 'ef': 'e', 'sa': 'st', 'ea': 'et'} 6 | 7 | broadcast_types = [{'id': bt[0], 'label': bt[1], 'reduce': bt[2], 'grid_x': bt[3], 'grid_y': bt[4]} for bt in [ 8 | ('s', 'To self (no target)', 's', 0, 0), 9 | ('e', 'To others (no target)', 's', 0, 1), 10 | ('t', 'To target (target is other)', 'e', 0, 2), 11 | ('st', 'To self (target is other)', 's', 1, 0), 12 | ('et', 'To others (target is other)', 'e', 1, 1), 13 | ('sf', 'To self (target is self)', 's', 2, 0), 14 | ('ef', 'To others (target is self)', 'e', 2, 1), 15 | ('sa', 'To self (target is not living)', 'st', 3, 0), 16 | ('ea', 'To environment (target is not living)', 'et', 3, 1)]] 17 | 18 | broadcast_tokens = [{'id': token_id, 'token': token} for token_id, token in [ 19 | ('n', 'Subject name'), 20 | ('N', 'Target name'), 21 | ('e', 'Subject pronoun'), 22 | ('E', 'Target pronoun'), 23 | ('s', 'Subject possessive pronoun'), 24 | ('S', 'Target possessive pronoun'), 25 | ('m', 'Subject objective pronoun'), 26 | ('M', 'Target objective pronoun'), 27 | ('f', 'Subject self pronoun'), 28 | ('F', 'Target self pronoun'), 29 | ('a', 'Absolute possessive subj'), 30 | ('A', 'Absolute possessive targ'), 31 | ('v', 'Action/verb')]] 32 | 33 | token_pattern = re.compile('\$([nNeEsSmMfFaA])') 34 | 35 | 36 | def substitute(message, source=None, target=None, verb=None, **ext_fmt): 37 | if source: 38 | s_name = getattr(source, 'name', source) 39 | s_sub, s_obj, s_poss, s_self, s_abs = pronouns[getattr(source, 'sex', 'none')] 40 | else: 41 | s_name = s_sub = s_obj = s_poss = s_self = s_abs = None 42 | if target: 43 | t_name = getattr(target, 'name', target) 44 | t_sub, t_obj, t_poss, t_self, t_abs = pronouns[getattr(target, 'sex', 'none')] 45 | else: 46 | t_name = t_sub = t_obj = t_poss = t_self = t_abs = None 47 | 48 | result = message.format(n=s_name, N=t_name, e=s_sub, E=t_sub, s=s_poss, S=t_poss, 49 | m=s_obj, M=t_obj, f=s_self, F=t_self, a=s_abs, A=t_abs, 50 | v=verb, **ext_fmt) 51 | return result 52 | 53 | 54 | class BroadcastMap: 55 | def __init__(self, **kwargs): 56 | for key, value in kwargs.items(): 57 | value = token_pattern.sub(r'{\1}', value) 58 | setattr(self, key, value) 59 | 60 | def __getitem__(self, msg_key): 61 | while True: 62 | msg = getattr(self, msg_key, None) 63 | if msg: 64 | return msg 65 | msg_key = defaults.get(msg_key, None) 66 | if not msg_key: 67 | return "[EMPTY]" 68 | 69 | 70 | class Broadcast: 71 | def __init__(self, broadcast_map=None, source=None, target=None, display='default', 72 | silent=False, verb=None, ext_fmt=None, **kwargs): 73 | if broadcast_map: 74 | self.broadcast_map = broadcast_map 75 | else: 76 | self.broadcast_map = BroadcastMap(**kwargs) 77 | self.source = source 78 | self.target = target 79 | self.display = display 80 | self.silent = silent 81 | self.verb = verb 82 | self.ext_fmt = ext_fmt if ext_fmt else {} 83 | 84 | def translate(self, observer): 85 | if self.silent and observer == self.source: 86 | return None 87 | if hasattr(self.broadcast_map, 'raw'): 88 | return self.broadcast_map['raw'] 89 | if not self.target: 90 | if not self.source or self.source == observer: 91 | return self.substitute('s') 92 | if self.target == self.source: 93 | if self.source == observer: 94 | return self.substitute('sf') 95 | return self.substitute('ef') 96 | if self.target == observer: 97 | return self.substitute('t') 98 | if not self.target: 99 | return self.substitute('e') 100 | if getattr(self.target, 'living', False): 101 | if self.source == observer: 102 | return self.substitute('st') 103 | return self.substitute('et') 104 | if self.source == observer: 105 | return self.substitute('sa') 106 | return self.substitute('ea') 107 | 108 | def substitute(self, version): 109 | return substitute(self.broadcast_map[version], self.source, self.target, self.verb, **self.ext_fmt) 110 | -------------------------------------------------------------------------------- /lampmud/lpmud/combat/fight.py: -------------------------------------------------------------------------------- 1 | from lampost.di.resource import Injected, module_inject 2 | from lampost.gameops.action import ActionError 3 | 4 | from lampmud.lpmud.combat.core import consider_level 5 | 6 | log = Injected('log') 7 | ev = Injected('dispatcher') 8 | module_inject(__name__) 9 | 10 | 11 | chase_time = 120 12 | 13 | 14 | class FightStats(): 15 | def __init__(self, con_level): 16 | self.attack_results = {} 17 | self.defend_results = {} 18 | self.con_level = con_level 19 | self.last_exit = None 20 | self.last_seen = ev.current_pulse 21 | 22 | 23 | class Fight(): 24 | hunt_timer = None 25 | current_target = None 26 | 27 | def __init__(self, me): 28 | self.me = me 29 | self.opponents = {} 30 | 31 | def update_skills(self): 32 | self.attacks = [attack for attack in self.me.skills.values() if getattr(attack, 'msg_class', None) == 'attacked'] 33 | self.attacks.sort(key=lambda x: x.points_per_pulse(self.me), reverse=True) 34 | self.defenses = [defense for defense in self.me.skills.values() if defense.template_id == 'defense' and not defense.auto_start] 35 | self.consider = self.me.considered() 36 | 37 | def add(self, opponent): 38 | try: 39 | self.opponents[opponent].last_seen = ev.current_pulse 40 | except KeyError: 41 | self.opponents[opponent] = FightStats(consider_level(self.consider, opponent.considered())) 42 | self.me.check_fight() 43 | 44 | def end(self, opponent, victory): 45 | try: 46 | del self.opponents[opponent] 47 | if opponent.last_opponent == self.me: 48 | opponent.last_opponent = None 49 | except KeyError: 50 | log.warn("Removing opponent not in fight") 51 | 52 | def end_all(self): 53 | for opponent in list(self.opponents.keys()): 54 | self.end(opponent, False) 55 | opponent.end_combat(self.me, True) 56 | self.clear_hunt_timer() 57 | 58 | def check_follow(self, opponent, ex): 59 | try: 60 | stats = self.opponents[opponent] 61 | stats.last_exit = ex 62 | stats.last_seen = ev.current_pulse 63 | self.select_action() 64 | except KeyError: 65 | pass 66 | 67 | def clear_hunt_timer(self): 68 | if self.hunt_timer: 69 | ev.unregister(self.hunt_timer) 70 | del self.hunt_timer 71 | 72 | def select_action(self): 73 | if not self.opponents: 74 | return 75 | self.clear_hunt_timer() 76 | local_opponents = [opponent for opponent in self.opponents.keys() if opponent.env == self.me.env] 77 | if local_opponents: 78 | local_opponents.sort(key=lambda opponent: opponent.health) 79 | self.select_attack(local_opponents[0]) 80 | else: 81 | self.try_chase() 82 | 83 | def select_attack(self, opponent): 84 | next_available = 100000 85 | for attack in self.attacks: 86 | available = attack.available 87 | if available > 0: 88 | next_available = min(available, next_available) 89 | continue 90 | try: 91 | self.me.check_costs(attack.costs) 92 | attack.validate(self.me, opponent) 93 | except ActionError: 94 | continue 95 | self.me.last_opponent = opponent 96 | self.me.start_action(attack, {'target': opponent, 'source': self.me}) 97 | return 98 | # Try again when another skill because available 99 | if next_available < 10000: 100 | ev.register_once(self.me.check_fight, next_available) 101 | 102 | def try_chase(self): 103 | stale_pulse = ev.future_pulse(-chase_time) 104 | removed = [opponent for opponent, stats in self.opponents.items() if stats.last_seen < stale_pulse] 105 | for opponent in removed: 106 | self.end(opponent, False) 107 | if not self.opponents: 108 | return 109 | for stats in sorted(self.opponents.values(), key=lambda x: x.last_seen, reverse=True): 110 | if stats.con_level < 1 and stats.last_exit in self.me.env.action_providers: 111 | try: 112 | self.me.start_action(stats.last_exit, {'source': self.me}) 113 | break 114 | except ActionError: 115 | pass 116 | self.hunt_timer = ev.register_p(self.select_action, seconds=10) 117 | -------------------------------------------------------------------------------- /webclient/common/js/autofill-event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Autofill event polyfill ##version:1.0.0## 3 | * (c) 2014 Google, Inc. 4 | * License: MIT 5 | */ 6 | (function(window) { 7 | var $ = window.jQuery || window.angular.element; 8 | var rootElement = window.document.documentElement, 9 | $rootElement = $(rootElement); 10 | 11 | addGlobalEventListener('change', markValue); 12 | addValueChangeByJsListener(markValue); 13 | 14 | $.prototype.checkAndTriggerAutoFillEvent = jqCheckAndTriggerAutoFillEvent; 15 | 16 | // Need to use blur and not change event 17 | // as Chrome does not fire change events in all cases an input is changed 18 | // (e.g. when starting to type and then finish the input by auto filling a username) 19 | addGlobalEventListener('blur', function(target) { 20 | // setTimeout needed for Chrome as it fills other 21 | // form fields a little later... 22 | window.setTimeout(function() { 23 | findParentForm(target).find('input').checkAndTriggerAutoFillEvent(); 24 | }, 20); 25 | }); 26 | 27 | function DOMContentLoadedListener() { 28 | // mark all values that are present when the DOM is ready. 29 | // We don't need to trigger a change event here, 30 | // as js libs start with those values already being set! 31 | forEach(document.getElementsByTagName('input'), markValue); 32 | 33 | // The timeout is needed for Chrome as it auto fills 34 | // login forms some time after DOMContentLoaded! 35 | window.setTimeout(function() { 36 | $rootElement.find('input').checkAndTriggerAutoFillEvent(); 37 | }, 200); 38 | } 39 | 40 | // IE8 compatibility issue 41 | if(!window.document.addEventListener){ 42 | window.document.attachEvent('DOMContentLoaded', DOMContentLoadedListener); 43 | }else{ 44 | window.document.addEventListener('DOMContentLoaded', DOMContentLoadedListener, false); 45 | } 46 | 47 | return; 48 | 49 | // ---------- 50 | 51 | function jqCheckAndTriggerAutoFillEvent() { 52 | var i, el; 53 | for (i=0; i 0) { 90 | forEach(this, function(el) { 91 | listener(el, newValue); 92 | }); 93 | } 94 | return res; 95 | }; 96 | } 97 | 98 | function addGlobalEventListener(eventName, listener) { 99 | // Use a capturing event listener so that 100 | // we also get the event when it's stopped! 101 | // Also, the blur event does not bubble. 102 | if(!rootElement.addEventListener){ 103 | rootElement.attachEvent(eventName, onEvent); 104 | }else{ 105 | rootElement.addEventListener(eventName, onEvent, true); 106 | } 107 | 108 | function onEvent(event) { 109 | var target = event.target; 110 | listener(target); 111 | } 112 | } 113 | 114 | function findParentForm(el) { 115 | while (el) { 116 | if (el.nodeName === 'FORM') { 117 | return $(el); 118 | } 119 | el = el.parentNode; 120 | } 121 | return $(); 122 | } 123 | 124 | function forEach(arr, listener) { 125 | if (arr.forEach) { 126 | return arr.forEach(listener); 127 | } 128 | var i; 129 | for (i=0; i maxLines) { 36 | self.display.splice(0, self.display.length - maxLines); 37 | } 38 | } 39 | 40 | function updateDisplay(display) { 41 | var lines = display.lines; 42 | for (var i = 0; i < lines.length; i++) { 43 | displayLine(lines[i]); 44 | } 45 | unreadCount += lines.length; 46 | jQuery('title').text('[' + unreadCount + '] ' + title); 47 | lpEvent.dispatch("display_update"); 48 | } 49 | 50 | function channelSubscribe(channel) { 51 | self.channels[channel.id] = channel.messages; 52 | lpEvent.dispatch("sort_channels"); 53 | } 54 | 55 | function channelUnsubscribe(channel_id) { 56 | delete self.channels[channel_id]; 57 | lpEvent.dispatch("sort_channels"); 58 | } 59 | 60 | function updateChannel(channelMessage) { 61 | self.channels[channelMessage.id].push(channelMessage); 62 | updateDisplay({lines: [{text: channelMessage.text, display: channelMessage.id.split("_")[0] + "_channel"}]}); 63 | } 64 | 65 | function setUser(data) { 66 | self.userId = data.user_id; 67 | self.playerIds = data.player_ids; 68 | self.notifies = data.notifies; 69 | if (data.password_reset) { 70 | lpEvent.dispatch('password_reset'); 71 | } 72 | } 73 | 74 | function clearUnread() { 75 | unreadCount = 0; 76 | jQuery('title').text(title); 77 | } 78 | 79 | this.adjustLine = function (line, display) { 80 | display = display || line.display; 81 | var lineDisplay = self.userDisplays[display] || self.defaultDisplays[display]; 82 | if (!lineDisplay) { 83 | return; 84 | } 85 | if (line.text == 'HRT') { 86 | line.style = {height: '2px', backgroundColor: lineDisplay.color, marginTop: '6px', marginBottom: '3px', marginRight: '3px'}; 87 | line.text = ''; 88 | } else if (line.text == "HRB") { 89 | line.style = {height: '2px', backgroundColor: lineDisplay.color, marginTop: '3px', marginBottom: '6px', marginRight: '3px'}; 90 | line.text = ''; 91 | } else { 92 | line.style = {color: lineDisplay.color}; 93 | } 94 | }; 95 | 96 | lpEvent.register('client_config', function (data) { 97 | self.defaultDisplays = data.default_displays; 98 | }); 99 | 100 | lpEvent.register("login", function (data) { 101 | setUser(data); 102 | self.playerName = data.name; 103 | self.userDisplays = data.displays; 104 | self.immLevel = data.imm_level; 105 | self.playerId = self.playerName.toLocaleLowerCase(); 106 | self.validTabs = ['status', 'channel', 'messages', 'playerList']; 107 | self.messages = data.messages; 108 | 109 | lpUtil.intSort(self.messages, 'msg_id'); 110 | }, null, -100); 111 | 112 | lpEvent.register('player_update', function(data) { 113 | self.immLevel = data.imm_level; 114 | }, null, -100); 115 | 116 | lpEvent.register("user_login", setUser, null, -100); 117 | lpEvent.register("display", updateDisplay, null, -100); 118 | lpEvent.register("channel", updateChannel, null, -100); 119 | lpEvent.register("channel_subscribe", channelSubscribe, null, -100); 120 | lpEvent.register("channel_unsubscribe", channelUnsubscribe, null, -100); 121 | lpEvent.register("user_activity", clearUnread); 122 | lpEvent.register("status", function (status) { 123 | self.status = status; 124 | }); 125 | lpEvent.register("logout", clear, null, -100); 126 | 127 | lpEvent.register("new_message", function (message) { 128 | self.messages.push(message); 129 | }, null, -100); 130 | 131 | lpEvent.register("player_list", function (playerList) { 132 | self.playerList = playerList; 133 | lpEvent.dispatch('player_list_update'); 134 | }); 135 | 136 | lpEvent.register("display_line", function(line, display) { 137 | updateDisplay({lines: [{text: line, display: display}]}); 138 | }) 139 | 140 | }]); 141 | 142 | --------------------------------------------------------------------------------