File: Combat.py

package info (click to toggle)
cyphesis-cpp 0.5.16-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 5,084 kB
  • ctags: 3,627
  • sloc: cpp: 30,418; python: 4,812; xml: 4,674; sh: 4,118; makefile: 902; ansic: 617
file content (151 lines) | stat: -rw-r--r-- 6,140 bytes parent folder | download
1
2
3
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
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#This file is distributed under the terms of the GNU General Public license.
#Copyright (C) 2005-2006 Al Riddoch (See the file COPYING for details).

from atlas import *
from physics import *
from Quaternion import Quaternion
from Vector3D import Vector3D

try:
  from random import *
except ImportError:
  from whrandom import *

from cyphesis.Thing import Thing

import server

class Combat(Thing):
    """A very simple combat system example."""
    def attack_operation(self, op):
        """ The attack op is FROM the the character that initiated combat which
            we term the attacker, TO the character that is attacker which we
            term the defender. We store the IDs of both. """
        # Check if the attacked characters stamina is too low for combat
        if self.character.stamina < 0.1:
            # print "Aborting defender stamina low"
            self.irrelevant()
            return
        assert(op.from_ != op.to)
        if op.to != self.character.id:
            self.oponent = op.to
            # print "Attack operation is not to this character"
            # We have initiative
        else:
            self.oponent = op.from_
            # print "Attack operation is to this character"
            self.surprise = True
            # We do not have initiative
        # Attach this task to the attacker. Its already implicitly attached
        # to the defender who owns this task.
        a=server.world.get_object(self.oponent)
        # Check if the attacking characters stamina is too low for combat
        if not a or a.stamina < 0.1:
            self.irrelevant()
            return
        # a.set_task(self.cppthing)
        self.square_range = 25
    def tick_operation(self, op):
        """ This method is called repeatedly, each time a combat turn occurs.
            In this example the interval is fixed, but it can be varied.
            self.attacker is the ID of the character that initiated the combat
            self.defender is the ID of the character that was initially
            attacked The self.attack flag is used to alternate the attack from
            one combatant to the other. """
        # if self.count() < 2:
            # print "Someone has dropped out"
            # self.irrelevant()
            # return

        assert(self.character.id == op.to)

        if self.character.stamina <= 0:
            # print "I am exhausted"
            self.irrelevant()
            return

        attacker = self.character
        if not attacker:
            sys.stderr.write("Attacker owning combat task destroyed, but task still running")
            self.irrelevant()
            return

        if attacker.stamina <= 0:
            # print "Attacker exhausted"
            self.irrelevant()
            return

        defender = server.world.get_object(self.oponent)
        if not defender:
            # print "No defender"
            self.irrelevant()
            return

        if hasattr(self, 'surprise') and self.surprise:
            # print 'Surprised!'
            self.surprise = False
            return self.next_tick(0.75 + uniform(0,0.25))

        if square_distance(self.character.location, defender.location) > self.square_range:
            return self.next_tick(1.75 + uniform(0,0.25))

        a=self.character.id
        d=self.oponent

        # A very simple formula is used to determine the damage done
        damage = (attacker.statistics.attack / defender.statistics.defence) / uniform(2,10)
        # Damage is counted against stamina, to ensure combat is non lethal,
        # and make recovery easier.
        stamina=defender.stamina-damage
        if stamina<0: stamina=0
        set_arg=Entity(self.oponent, stamina=stamina)

        # We send 3 operations to indicate what is going on. The imginary ops
        # provide emotes for the actions. The sight(attack) operation
        # indicates that a singleshot animation of attacking should be
        # triggered on the attacker.
        attacker.send_world(Operation("imaginary", Entity(description="hits for massive damage."), to=attacker))
        attacker.send_world(Operation("sight", Operation("attack", to=d, from_=a)))
        defender.send_world(Operation("imaginary", Entity(description="defends skillfully."), to=defender))

        # If the defenders stamina has reached zero, combat is over, and emotes
        # are sent to indicate this.
        if stamina <= 0:
            set_arg.status = defender.status - 0.1
            defender.send_world(Operation("imaginary", Entity(description="has been defeated"), to=defender))
            defender.send_world(Operation("sight", Operation("collapse", from_=d)))
            attacker.send_world(Operation("imaginary", Entity(description="is victorious"), to=attacker))
            self.irrelevant()

        res=Message()
        # This set op makes the change to defenders stamina, and a small health
        # change if they have been defeated
        res.append(Operation("set", set_arg, to=defender))

        # Turn the attacker to face the defender. This has to go through
        # the mind2body interface, so it does not interrupt what the
        # the character is doing.
        faceop=self.face(defender)
        if faceop:
            faceop=attacker.mind2body(faceop)
            if faceop:
                res.append(faceop)

        # Don't return the following tick op if this task is now complete
        if self.obsolete():
            return res

        # Schedule a new tick op
        res.append(self.next_tick(1.75 + uniform(0,0.25)))
        return res
    def face(self, other):
        """ Turn to face that another character, ensuring that
            we are facing the character we are hitting """
        vector = distance_to(self.character.location, other.location)
        vector.z = 0
        if vector.square_mag() < 0.1:
            return
        vector = vector.unit_vector()
        newloc = Location(self.character.location.parent)
        newloc.orientation = Quaternion(Vector3D(1,0,0), vector)
        return Operation("move", Entity(self.character.id, location=newloc))