summaryrefslogtreecommitdiff
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/__init__.py0
-rw-r--r--gui/__init__.pycbin0 -> 154 bytes
-rw-r--r--gui/__pycache__/__init__.cpython-37.pycbin0 -> 154 bytes
-rw-r--r--gui/__pycache__/managui.cpython-37.pycbin0 -> 6859 bytes
-rw-r--r--gui/chatlog.py86
-rw-r--r--gui/handlers.py49
-rw-r--r--gui/handlers.pycbin0 -> 2190 bytes
-rw-r--r--gui/icon.pngbin0 -> 69430 bytes
-rw-r--r--gui/managui.kv155
-rw-r--r--gui/managui.py192
-rw-r--r--gui/managui.pycbin0 -> 9104 bytes
-rw-r--r--gui/pathfind.py91
-rw-r--r--gui/plist.py54
-rw-r--r--gui/tmxmap.py175
14 files changed, 802 insertions, 0 deletions
diff --git a/gui/__init__.py b/gui/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gui/__init__.py
diff --git a/gui/__init__.pyc b/gui/__init__.pyc
new file mode 100644
index 0000000..de4ecef
--- /dev/null
+++ b/gui/__init__.pyc
Binary files differ
diff --git a/gui/__pycache__/__init__.cpython-37.pyc b/gui/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..babd95b
--- /dev/null
+++ b/gui/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/gui/__pycache__/managui.cpython-37.pyc b/gui/__pycache__/managui.cpython-37.pyc
new file mode 100644
index 0000000..c15bd16
--- /dev/null
+++ b/gui/__pycache__/managui.cpython-37.pyc
Binary files differ
diff --git a/gui/chatlog.py b/gui/chatlog.py
new file mode 100644
index 0000000..aca9564
--- /dev/null
+++ b/gui/chatlog.py
@@ -0,0 +1,86 @@
+from kivy.event import EventDispatcher
+from kivy.uix.abstractview import AbstractView
+from kivy.properties import ListProperty, NumericProperty
+from kivy.adapters.simplelistadapter import SimpleListAdapter
+from kivy.utils import escape_markup
+from kivy.clock import Clock
+from kivy.uix.label import Label
+# from utils import log_method
+
+from textutils import (links_to_markup, replace_emotes, preprocess,
+ remove_formatting)
+
+
+class ChatLog(AbstractView, EventDispatcher):
+
+ def __init__(self, **kwargs):
+ # Check for adapter argument
+ if 'adapter' not in kwargs:
+ list_adapter = SimpleListAdapter(data=[], cls=Label)
+ kwargs['adapter'] = list_adapter
+
+ super(ChatLog, self).__init__(**kwargs)
+
+ self._views = []
+
+ populate = self._trigger_populate = Clock.create_trigger(
+ self._populate, -1)
+
+ fbind = self.fbind
+ fbind('adapter', populate)
+ fbind('size', populate)
+ fbind('pos', populate)
+
+ max_lines = NumericProperty(100)
+ cut_lines = NumericProperty(10)
+
+ def _populate(self, *args):
+ container = self.container
+ adapter = self.adapter
+ container.clear_widgets()
+ self._views = []
+
+ for index in range(adapter.get_count()):
+ item_view = adapter.get_view(index)
+ self.container.add_widget(item_view)
+ self._views.append(item_view)
+
+ container.height = self._container_height()
+
+ def _append(self, msg):
+ container = self.container
+ adapter = self.adapter
+ views = self._views
+ cl = self.cut_lines
+
+ if len(views) >= self.max_lines:
+ container.clear_widgets(views[:cl])
+ self._views = views[cl:]
+ adapter.data = adapter.data[cl:]
+
+ adapter.data.append(msg)
+ item_view = adapter.get_view(adapter.get_count() - 1)
+ item_view.texture_update()
+
+ self._views.append(item_view)
+ container.add_widget(item_view)
+
+ def _container_height(self, *args):
+ h = 0
+ for v in self._views:
+ h += v.height
+ return h
+
+ def append_message(self, msg):
+ msg = preprocess(msg, (replace_emotes,
+ remove_formatting))
+ msg = links_to_markup(escape_markup(msg))
+ self._append(msg)
+ self.container.height = self._container_height()
+ self.children[0].scroll_y = 0
+
+ def msg_converter(self, index, msg):
+ b = (index % 2) * 0.04
+ return {'text': msg,
+ 'width': self.width,
+ 'background_color': (0 + b, 0.17 + b, 0.21 + b, 1)}
diff --git a/gui/handlers.py b/gui/handlers.py
new file mode 100644
index 0000000..b6f9d0b
--- /dev/null
+++ b/gui/handlers.py
@@ -0,0 +1,49 @@
+
+import net.mapserv as mapserv
+from loggers import debuglog
+from utils import extends
+
+
+__all__ = ['app']
+
+
+map_name = ""
+app = None
+
+
+@extends('smsg_whisper_response')
+def send_whisper_result(data):
+ if data.code == 0:
+ last_nick = mapserv.last_whisper['to']
+ app.root.chat_input.text = '/w "{}" '.format(last_nick)
+ app.root.chat_input.focus = True
+
+
+@extends('smsg_player_warp')
+def player_warp(data):
+ mapserv.cmsg_map_loaded()
+ m = "[warp] {} ({},{})".format(data.map, data.x, data.y)
+ debuglog.info(m)
+
+
+@extends('smsg_char_map_info')
+def char_map_info(data):
+ global map_name
+ map_name = data.map_name
+
+
+@extends('smsg_map_login_success')
+def map_login_success(data):
+ m = "[map] {} ({},{})".format(map_name, data.coor.x, data.coor.y)
+ debuglog.info(m)
+ mapserv.server.raw = True
+ mapserv.cmsg_map_loaded()
+
+
+@extends('smsg_connection_problem')
+def connection_problem(data):
+ error_codes = {
+ 2 : "Account already in use"
+ }
+ msg = error_codes.get(data.code, str(data.code))
+ debuglog.error('Connection problem: %s', msg)
diff --git a/gui/handlers.pyc b/gui/handlers.pyc
new file mode 100644
index 0000000..8532460
--- /dev/null
+++ b/gui/handlers.pyc
Binary files differ
diff --git a/gui/icon.png b/gui/icon.png
new file mode 100644
index 0000000..3bea05d
--- /dev/null
+++ b/gui/icon.png
Binary files differ
diff --git a/gui/managui.kv b/gui/managui.kv
new file mode 100644
index 0000000..7e41701
--- /dev/null
+++ b/gui/managui.kv
@@ -0,0 +1,155 @@
+#:import la kivy.adapters.simplelistadapter
+#:import ChatLog gui.chatlog.ChatLog
+#:import PlayersList gui.plist.PlayersList
+#:import PlayersListItem gui.plist.PlayersListItem
+
+
+<PlayersListItem>:
+
+ canvas.before:
+ Color:
+ rgba: 0.05, 0.05, 0.05, 1
+ Line:
+ points: [ self.x, self.y, self.x+self.width, self.y ]
+
+ Label:
+ text: root.nick
+
+
+<PlayersList>:
+
+ canvas.before:
+ Color:
+ rgba: .25, .1, .9, 1
+ Rectangle:
+ pos: self.pos
+ size: self.size
+
+
+<RootWidget>:
+ mobile: True if self.width < dp(600) else False
+
+ canvas.before:
+ Color:
+ rgba: 0.01, 0.33, 0.32, 1
+ Rectangle:
+ pos: self.pos
+ size: self.size
+
+ messages_log: id_chat_log
+ chat_input: id_chat_input
+ players_list: id_players_list
+
+ BoxLayout:
+ orientation: 'horizontal'
+
+ BoxLayout:
+ orientation: 'vertical'
+
+ ChatLog:
+ id: id_chat_log
+ adapter:
+ la.SimpleListAdapter(
+ data=['Welcome to [ref=https://bitbucket.org/rumly111/manachat/][color=0000ff]ManaChat[/color][/ref]. Press F1 to show settings. Press ESCAPE to toggle menu.'],
+ template='ChatLogItem',
+ args_converter=self.msg_converter)
+
+ TextInput:
+ id: id_chat_input
+ size_hint_y: None
+ height: '50dp'
+ # focus: True
+ multiline: False
+ on_text_validate: root.on_command_enter(args)
+
+ PlayersList:
+ id: id_players_list
+ size_hint_x: None
+ width: '150dp'
+
+
+[ChatLogItem@Label]:
+
+ canvas.before:
+ Color:
+ rgba: ctx.background_color
+ Rectangle:
+ pos: self.pos
+ size: self.size
+
+ text: ctx.text
+ width: ctx.width
+ text_size: self.width, None
+ size_hint: None, None
+ height: self.texture_size[1] + 10
+ markup: True
+ on_ref_press: app.open_link(args[1])
+
+
+<ChatLog>:
+ container: container
+ ScrollView:
+ pos: root.pos
+ do_scroll_x: False
+ GridLayout:
+ cols: 1
+ id: container
+ size_hint_y: None
+
+
+<AboutPopup@Popup>:
+ title_size: '14dp'
+ title: 'About'
+ size_hint: 0.9, None
+ height: '140dp'
+
+ Label:
+ id: lbl
+ text_size: self.width, None
+ size_hint_y: None
+ height: self.texture_size[1]
+ text:
+ '''ManaChat is a multi-purpose chat client for The Mana World MMORPG
+ Author: Joseph Botosh <rumly111@gmail.com> (TMW nickname: Travolta)
+ Licence: Gnu General Public Licence, rev. 2
+ Homepage: [ref=https://bitbucket.org/rumly111/manachat/]https://bitbucket.org/rumly111/manachat/[/ref]'''
+
+ markup: True
+ on_ref_press: app.open_link(args[1])
+
+
+<MenuPopup@Popup>:
+ title_size: '14dp'
+ title: 'ManaChat'
+ auto_dismiss: False
+ size_hint: None, None
+ width: "200dp"
+ height: "250dp"
+
+ BoxLayout:
+ spacing: "2dp"
+ padding: "2dp"
+ orientation: "vertical"
+
+ Button:
+ text: "Connect"
+ on_press: app.reconnect()
+
+ Button:
+ text: "Config"
+ on_press: app.open_settings()
+
+ Button:
+ text: "About"
+ on_press: app.show_about()
+
+ Button:
+ text: "Exit"
+ on_press: app.stop()
+
+
+<SettingPassword>:
+ Label:
+ text: '*' * len(root.value) if root.value else ''
+ pos: root.pos
+ font_size: '15sp'
diff --git a/gui/managui.py b/gui/managui.py
new file mode 100644
index 0000000..d6dd79a
--- /dev/null
+++ b/gui/managui.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python2
+# -- coding: utf-8 --
+
+import asyncore
+import logging
+import webbrowser
+
+import kivy
+kivy.require('1.9.1')
+
+from kivy.app import App
+from kivy.clock import Clock
+from kivy.core.window import Window
+from kivy.properties import BooleanProperty
+from kivy.uix.floatlayout import FloatLayout
+from kivy.uix.popup import Popup
+
+from kivy.config import ConfigParser
+config = ConfigParser()
+config.read("manachat.ini")
+
+import monsterdb
+import itemdb
+import net
+import net.mapserv as mapserv
+import gui.handlers as handlers
+import plugins
+from commands import process_line
+from net.onlineusers import OnlineUsers
+from loggers import netlog, debuglog
+from logicmanager import logic_manager
+
+
+class DebugLogHandler(logging.Handler):
+
+ def __init__(self, app, **kwargs):
+ self.app = app
+ super(self.__class__, self).__init__(**kwargs)
+
+ def emit(self, record):
+ msg = self.format(record)
+ self.app.root.messages_log.append_message(msg)
+
+
+class MenuPopup(Popup):
+ visible = BooleanProperty(False)
+
+ def on_open(self, *args):
+ self.visible = True
+
+ def on_dismiss(self, *args):
+ self.visible = False
+
+
+class AboutPopup(Popup):
+ pass
+
+
+class RootWidget(FloatLayout):
+ mobile = BooleanProperty(False)
+
+ def _focus_chat_input(self, dt):
+ self.chat_input.focus = True
+
+ def on_command_enter(self, *args):
+ process_line(self.chat_input.text.encode('utf-8'))
+ self.chat_input.text = ''
+ Clock.schedule_once(self._focus_chat_input, 0.1) # dirty hack :^)
+
+
+class ManaGuiApp(App):
+ use_kivy_settings = BooleanProperty(False)
+
+ def hook_keyboard(self, window, key, *largs):
+ if key == 27:
+ self.show_menu(not self._menu_popup.visible)
+ # self.stop()
+ return True
+ return False
+
+ def on_start(self):
+ if config.getboolean('Other', 'log_network_packets'):
+ import os
+ import tempfile
+
+ logfile = os.path.join(tempfile.gettempdir(), "netlog.txt")
+ netlog.setLevel(logging.INFO)
+ fh = logging.FileHandler(logfile, mode="w")
+ fmt = logging.Formatter("[%(asctime)s] %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S")
+ fh.setFormatter(fmt)
+ netlog.addHandler(fh)
+
+ monsterdb.read_monster_db()
+ itemdb.load_itemdb('itemdb.txt')
+
+ dbgh = DebugLogHandler(self)
+ dbgh.setFormatter(logging.Formatter("[%(asctime)s] %(message)s",
+ datefmt="%H:%M"))
+ debuglog.addHandler(dbgh)
+ debuglog.setLevel(logging.INFO)
+
+ net2 = DebugLogHandler(self)
+ net2.setFormatter(logging.Formatter("[%(asctime)s] %(message)s",
+ datefmt="%H:%M"))
+ net2.setLevel(logging.ERROR)
+ netlog.addHandler(net2)
+
+ plugins.load_plugins(config)
+
+ handlers.app = self
+
+ import chat
+ chat.pp_actions = ()
+
+ # self.reconnect()
+
+ Clock.schedule_once(self.update_online_list, 0.2)
+ Clock.schedule_interval(self.update_online_list, 35)
+ Clock.schedule_interval(self.update_loop, 0)
+ Clock.schedule_once(self.show_menu, 1.5)
+
+ def build(self):
+ self.icon = 'icon.png'
+ Window.bind(on_keyboard=self.hook_keyboard)
+ return RootWidget()
+
+ def build_settings(self, settings):
+ from kivy.uix.settings import SettingString
+
+ class SettingPassword(SettingString):
+ def _create_popup(self, instance):
+ SettingString._create_popup(self, instance)
+ self.textinput.password = True
+
+ def add_widget(self, *largs):
+ if self.content is not None:
+ self.content.clear_widgets()
+ return SettingString.add_widget(self, *largs)
+
+ settings.register_type('password', SettingPassword)
+ settings.add_json_panel('ManaChat', config,
+ filename='manachat.json')
+
+ def update_loop(self, *l):
+ asyncore.loop(timeout=0, count=10)
+ logic_manager.tick()
+
+ def on_pause(self):
+ return True
+
+ def on_stop(self):
+ Clock.unschedule(self.update_loop)
+ Clock.unschedule(self.update_online_list)
+
+ def open_link(self, link):
+ webbrowser.open(link)
+
+ def update_online_list(self, *l):
+ lst = OnlineUsers.dl_online_list(config.get('Other',
+ 'online_txt_url'))
+ if lst is not None:
+ lst.sort()
+ self.root.players_list.items = lst
+
+ def reconnect(self):
+ if mapserv.server is not None:
+ mapserv.cleanup()
+
+ net.login(host=config.get('Server', 'host'),
+ port=config.getint('Server', 'port'),
+ username=config.get('Player', 'username'),
+ password=config.get('Player', 'password'),
+ charname=config.get('Player', 'charname'))
+
+ if hasattr(self, '_menu_popup'):
+ self._menu_popup.dismiss()
+
+ def show_about(self):
+ AboutPopup().open()
+
+ def show_menu(self, show=True):
+ if not hasattr(self, '_menu_popup'):
+ self._menu_popup = MenuPopup()
+ if show:
+ self._menu_popup.open()
+ else:
+ self._menu_popup.dismiss()
+
+
+if __name__ == "__main__":
+ ManaGuiApp().run()
diff --git a/gui/managui.pyc b/gui/managui.pyc
new file mode 100644
index 0000000..74b595d
--- /dev/null
+++ b/gui/managui.pyc
Binary files differ
diff --git a/gui/pathfind.py b/gui/pathfind.py
new file mode 100644
index 0000000..2c870e1
--- /dev/null
+++ b/gui/pathfind.py
@@ -0,0 +1,91 @@
+import collections
+
+
+class Queue:
+ def __init__(self):
+ self.elements = collections.deque()
+
+ def empty(self):
+ return len(self.elements) == 0
+
+ def put(self, x):
+ self.elements.append(x)
+
+ def get(self):
+ return self.elements.popleft()
+
+
+class Graph:
+ def __init__(self, collisions):
+ self.collisions = collisions
+ self.width = len(collisions[0])
+ self.height = len(collisions)
+
+ def walkable(self, coor):
+ x, y = coor
+ if x < 0 or x > self.width - 1:
+ return False
+ if y < 0 or y > self.height - 1:
+ return False
+ if self.collisions[self.height - y - 1][x] > 0:
+ return False
+ return True
+
+ def neighbors(self, coor):
+ n = []
+ x, y = coor
+
+ for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
+ nx = x + dx
+ ny = y + dy
+ if self.walkable((nx, ny)):
+ n.append((nx, ny))
+
+ for dx, dy in ((-1, -1), (-1, 1), (1, -1), (1, 1)):
+ nx = x + dx
+ ny = y + dy
+ if (self.walkable((nx, ny)) and
+ self.walkable((nx, y)) and
+ self.walkable((x, ny))):
+ n.append((nx, ny))
+
+ return n
+
+
+def breadth_first_search(collisions, start, goal):
+ graph = Graph(collisions)
+ frontier = Queue()
+ frontier.put(start)
+ came_from = {}
+ came_from[start] = None
+
+ while not frontier.empty():
+ current = frontier.get()
+
+ if current == goal:
+ break
+
+ for next in graph.neighbors(current):
+ if next not in came_from:
+ frontier.put(next)
+ came_from[next] = current
+
+ current = goal
+ path = [current]
+ while current != start:
+ current = came_from[current]
+ path.append(current)
+ path.reverse()
+
+ # simplify path
+ spath = path[:1]
+ for i in range(1, len(path) - 1):
+ px, py = path[i - 1]
+ cx, cy = path[i]
+ nx, ny = path[i + 1]
+ if nx - cx == cx - px and ny - cy == cy - py:
+ continue
+ spath.append(path[i])
+ spath.append(path[-1])
+
+ return spath
diff --git a/gui/plist.py b/gui/plist.py
new file mode 100644
index 0000000..a009a32
--- /dev/null
+++ b/gui/plist.py
@@ -0,0 +1,54 @@
+from kivy.app import App
+from kivy.adapters.listadapter import ListAdapter
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.floatlayout import FloatLayout
+from kivy.properties import StringProperty, ListProperty
+from kivy.uix.listview import ListView, ListItemLabel
+
+
+class PlayersListItem(BoxLayout, ListItemLabel):
+ nick = StringProperty(allow_none=False)
+
+ def on_touch_down(self, touch):
+ if not self.collide_point(*touch.pos):
+ return False
+ touch.grab(self)
+ return True
+
+ def on_touch_up(self, touch):
+ if not self.collide_point(*touch.pos):
+ return False
+ if touch.grab_current is self:
+ app = App.get_running_app()
+ app.root.chat_input.text = '/w "{}" '.format(self.nick)
+ app.root.chat_input.focus = True
+ touch.ungrab(self)
+ return True
+
+
+class PlayersList(FloatLayout):
+ items = ListProperty([])
+
+ def __init__(self, **kwargs):
+ super(PlayersList, self).__init__(**kwargs)
+
+ player_args_coverter = lambda row_index, nick: {"nick" : nick,
+ "size_hint_y": None, "height": "30dp", "pos_hint_y": 0.9 }
+
+ list_adapter = ListAdapter(
+ data=["Ginaria", "Celestia"],
+ args_converter=player_args_coverter,
+ selection_mode='single',
+ allow_empty_selection=True,
+ cls=PlayersListItem)
+
+ list_view = ListView(adapter=list_adapter,
+ size_hint=(1.0, 1.0),
+ pos_hint={'center_x': 0.5})
+
+ def data_changed(instance, value):
+ list_adapter.data = value
+
+ self.bind(items=data_changed)
+
+ self.add_widget(list_view)
diff --git a/gui/tmxmap.py b/gui/tmxmap.py
new file mode 100644
index 0000000..800868e
--- /dev/null
+++ b/gui/tmxmap.py
@@ -0,0 +1,175 @@
+
+# import pytmx
+import zipfile
+from itertools import cycle, islice
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
+from kivy.core.image import Image as CoreImage
+from kivy.uix.widget import Widget
+from kivy.uix.image import Image
+from kivy.graphics.texture import Texture
+from kivy.animation import Animation
+from kivy.properties import (StringProperty, ObjectProperty,
+ NumericProperty, DictProperty)
+from kivy.logger import Logger
+
+from pathfind import breadth_first_search
+import net.mapserv as mapserv
+
+
+def kivy_image_loader(filename, colorkey, **kwargs):
+ texture = CoreImage(filename).texture
+
+ def loader(rect, flags):
+ x, y, w, h = rect
+ t = texture.get_region(x,
+ texture.height - y - h,
+ w, h)
+
+ if flags.flipped_diagonally:
+ t.flip_horizontal()
+ t.flip_vertical()
+ elif flags.flipped_vertically:
+ t.flip_vertical()
+ elif flags.flipped_horizontally:
+ t.flip_horizontal()
+
+ return t
+
+ return loader
+
+
+def create_map_texture(map_data, tile_size=4):
+ map_size = len(map_data[0]), len(map_data)
+ texture = Texture.create(size=(map_size[0] * tile_size,
+ map_size[1] * tile_size),
+ colorfmt='rgb')
+
+ c = cycle('\x45\x46\x47')
+ s = islice(c, 3 * 4000)
+ buf = b''.join(s)
+
+ for y, row in enumerate(map_data):
+ for x, cell in enumerate(row):
+ if cell > 0:
+ continue
+
+ texture.blit_buffer(buf, size=(tile_size, tile_size),
+ colorfmt='rgb',
+ pos=(x * tile_size, y * tile_size))
+
+ texture.flip_vertical()
+
+ return texture
+
+
+class BeingWidget(Widget):
+ anim = ObjectProperty(None)
+ name = StringProperty()
+
+
+class MapWidget(Image):
+ tile_size = NumericProperty(32)
+ player = ObjectProperty()
+ collisions = ObjectProperty(None)
+ current_attacks = DictProperty()
+ beings = DictProperty()
+ maps = {}
+
+ def load_map(self, name, *args):
+ if name not in self.maps:
+ Logger.info("Caching map %s", name)
+ zf = zipfile.ZipFile('mapdb.zip', 'r')
+ self.maps[name] = pickle.load(zf.open(name + '.pickle'))
+ zf.close()
+ # m = pytmx.TiledMap(filename=filename)
+ # data = m.get_layer_by_name('Collision').data
+ texture = create_map_texture(self.maps[name]['collisions'],
+ self.tile_size)
+ self.texture = texture
+ self.size = texture.size
+ self.collisions = self.maps[name]['collisions']
+
+ def to_game_coords(self, pos):
+ ts = self.tile_size
+ height = len(self.collisions)
+ x = int(pos[0] // ts)
+ y = height - int(pos[1] // ts) - 1
+ return x, y
+
+ def from_game_coords(self, pos):
+ ts = self.tile_size
+ height = len(self.collisions)
+ x = pos[0] * ts
+ y = (height - pos[1] - 1) * ts
+ return x, y
+
+ def move_being(self, being, gx, gy, speed=150):
+ ts = self.tile_size
+ ox = int(being.x // ts)
+ oy = int(being.y // ts)
+ gy = len(self.collisions) - gy - 1
+
+ try:
+ path = breadth_first_search(self.collisions,
+ (ox, oy), (gx, gy))
+ except KeyError:
+ path = []
+
+ if being.anim:
+ being.anim.stop(being)
+
+ being.anim = Animation(x=being.x, y=being.y, duration=0)
+
+ for i in range(len(path) - 1):
+ cx, cy = path[i + 1]
+ px, py = path[i]
+ distance = max(abs(cx - px), abs(cy - py))
+ being.anim += Animation(x=cx * ts, y=cy * ts,
+ duration=distance * speed / 1000.)
+
+ being.anim.start(being)
+
+ def get_attack_points(self, *bind_args):
+ points = []
+ try:
+ player_id = mapserv.server.char_id
+ except AttributeError:
+ player_id = 0
+ for (id1, id2) in self.current_attacks:
+ try:
+ # Temporary workaround
+ if id1 == player_id:
+ x1, y1 = self.player.pos
+ else:
+ x1, y1 = self.beings[id1].pos
+ if id2 == player_id:
+ x2, y2 = self.player.pos
+ else:
+ x2, y2 = self.beings[id2].pos
+ points.extend([x1, y1, x2, y2])
+ except:
+ pass
+
+ return points
+
+ def get_attack_endpoints(self, *bind_args):
+ points = []
+ try:
+ player_id = mapserv.server.char_id
+ except AttributeError:
+ player_id = 0
+ for (_, target) in self.current_attacks:
+ try:
+ if target == player_id:
+ x, y = self.player.pos
+ else:
+ x, y = self.beings[target].pos
+ points.extend([x, y])
+ except:
+ pass
+
+ return points