1--io.stderr:write("actor.lua get here\n")
2
3
4--Worktypes
5WT_NOTHING                = 0x0  --do nothing
6
7WT_FOLLOW                 = 0x1  --follow avatar (in party)
8
9WT_PLAYER                 = 0x2  --player mode
10
11WT_FRONT                  = 0x3  --combat front
12WT_REAR                   = 0x4  --combat rear
13WT_FLANK                  = 0x5  --combat flank
14WT_BERSERK                = 0x6  --combat berserk
15WT_RETREAT                = 0x7  --combat retreat
16WT_ASSAULT                = 0x8  --combat assault
17WT_FLEE                   = 0x9  --run away from party
18WT_LIKE                   = 0xa  --like the party. eg dog
19WT_UNFRIENDLY             = 0xb  --wander around but attack if any party member gets too close.
20WT_WANDER_NEAR_PLAYER     = 0xc  --guards use this.
21WT_TANGLE                 = 0xd  --tangle vine
22WT_STATIONARY             = 0xe  --stationary attack
23WT_GUARD_WALK_EAST_WEST   = 0xf
24WT_GUARD_WALK_NORTH_SOUTH = 0x10
25--11
26WT_GUARD_ARREST_PLAYER    = 0x12
27WT_UNK_13                 = 0x13 --FIXME I think this is repel undead
28
29WT_WALK_TO_LOCATION       = 0x86
30WT_FACE_NORTH             = 0x87
31WT_FACE_EAST              = 0x88
32WT_FACE_SOUTH             = 0x89
33WT_FACE_WEST              = 0x8a
34WT_WALK_NORTH_SOUTH       = 0x8b
35WT_WALK_EAST_WEST         = 0x8c
36WT_WALK_SOUTH_NORTH       = 0x8d
37WT_WALK_WEST_EAST         = 0x8e
38WT_WANDER_AROUND          = 0x8f
39WT_WORK                   = 0x90
40WT_SLEEP                  = 0x91
41
42
43WT_UNK_94                 = 0x94
44WT_PLAY_LUTE              = 0x95
45WT_BEG                    = 0x96
46
47WT_BELL_RINGER            = 0x98
48WT_BRAWLING               = 0x99
49WT_MOUSE                  = 0x9a
50WT_ATTACK_PARTY           = 0x9b
51
52
53wt_rear_max_monster = nil
54wt_rear_min_monster = nil
55wt_rear_max_party = nil
56wt_rear_min_party = nil
57
58combat_avg_x, combat_avg_y, party_avg_x, party_avg_y = -1, -1, -1, -1
59
60wt_front_target_actor = nil
61
62wt_num_monsters_near = 0
63
64g_time_stopped = false
65
66g_update_volcano = false;
67
68--Actor stats table
69--[objnum] = {str,dex,int,hp,dmg,alignment,can talk,drops blood,?,?,?,lives in water,flies-immune to tremor,repel undead,poisonous,strength_based,double dmg from fire,immune to magic (fire only),immune to poison,undead and immune to death spells,immune to sleep spell,{spell table},{weapon table},{armor table},{treasure table},exp_related see actor_hit()}
70actor_tbl = {
71--Acid Slug
72[364] = {5, 5, 2, 10, 1, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
73--Alligator
74[429] = {20, 10, 3, 30, 10, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, {}, {}, {}, {}, 6},
75--Ant, Giant
76[427] = {16, 15, 2, 10, 8, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, {}, {}, {}, {}, 3},
77--Avatar
78[410] = {25, 25, 25, 30, 4, ALIGNMENT_GOOD, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
79--Bat, Giant
80[344] = {5, 25, 4, 5, 6, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
81--Peasant
82[384] = {8, 8, 9, 6, 3, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {38,34}, {185}, {}, 0},
83--Bird
84[356] = {1, 28, 4, 3, 1, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
85----Child
86[381] = {8, 8, 9, 6, 2, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
87--Corpser
88[357] = {17, 6, 3, 40, 15, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
89--Cow
90[428] = {14, 7, 5, 10, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {129}, 0},
91--Cyclops
92[424] = {27, 8, 7, 40, 20, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {44,33,34}, {20,3,13}, {98}, 4},
93--Daemon
94[367] = {35, 26, 31, 100, 20, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, {17,128,34,80}, {}, {}, {}, 10},
95--Deer
96[350] = {11, 18, 5, 8, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {129}, 0},
97--Dog
98[349] = {5, 16, 8, 6, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
99--Dragon
100[411] = {40, 35, 27, 150, 30, ALIGNMENT_EVIL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, {34,83,68,96,119}, {}, {}, {98,88,88}, 12},
101--Drake
102[369] = {22, 22, 13, 50, 10, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, {34,68}, {}, {}, {88,88}, 4},
103--Woman
104[387] = {10, 14, 15, 8, 4, ALIGNMENT_NEUTRAL, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {38}, {185}, {88}, 0},
105--Farmer
106[385] = {15, 15, 10, 8, 4, ALIGNMENT_NEUTRAL, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {105,101}, {17}, {88}, 0},
107--Fighter
108[376] = {20, 17, 11, 20, 6, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {43,42}, {21,10,3,22}, {88,88}, 0},
109--Gargoyle, Winged
110[362] = {15, 32, 33, 50, 6, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, {68,65,69}, {46,41,47,49}, {18,1}, {58}, 5},
111--Gargoyle, Worker
112[363] = {25, 19, 8, 30, 15, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {34,49,37}, {1,9,18,20}, {}, 10},
113--Gazer
114[355] = {2, 10, 21, 20, 1, ALIGNMENT_EVIL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, {20,68,80}, {}, {}, {}, 0},
115--Ghost
116[352] = {1, 10, 9, 20, 12, ALIGNMENT_EVIL, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, {128,99}, {}, {}, {}, 0},
117--Gremlin
118[353] = {2, 26, 7, 5, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
119--Guard
120[382] = {9, 30, 9, 100, 8, ALIGNMENT_EVIL, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {42,47}, {22,10,3}, {88}, 0},
121--Headless
122[370] = {19, 13, 8, 20, 12, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {34,36}, {17,9}, {88}, 2},
123--Horse
124[430] = {22, 27, 7, 15, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
125--Horse with rider
126[431] = {22, 27, 7, 15, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
127--Hydrae
128[374] = {26, 11, 2, 50, 15, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, {69,96,98,68}, {}, {}, {}, 0},
129--Insects
130[343] = {1, 40, 1, 1, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, {}, {}, {}, {}, 0},
131--Jester
132[383] = {14, 16, 12, 8, 4, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {38,33}, {17}, {88}, 0},
133--Lord British
134[409] = {30, 30, 30, 255, 255, ALIGNMENT_GOOD, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 30},
135--Musician
136[386] = {12, 16, 14, 8, 4, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {41,33,43}, {19,1,9}, {88,158}, 0},
137--Mage
138[378] = {10, 14, 22, 30, 4, ALIGNMENT_EVIL, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {17,34,20,49,68,80,69,83}, {38}, {17,1}, {58,98,88}, 0},
139--Merchant
140[380] = {12, 12, 18, 8, 4, ALIGNMENT_EVIL, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {38,43}, {17,1}, {88}, 0},
141--Mimic
142[98] = {22, 9, 8, 30, 15, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, {18,80}, {}, {}, {98,98}, 5},
143--Mongbat
144[372] = {20, 27, 13, 30, 20, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {38,43,36,37}, {9,17,18}, {98,88}, 4},
145--Mouse
146[354] = {2, 25, 3, 5, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
147--Rabbit
148[359] = {3, 9, 3, 5, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
149--Rat, Giant
150[342] = {5, 13, 4, 8, 6, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, {}, {}, {}, {}, 0},
151--Reaper
152[347] = {21, 20, 16, 30, 20, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, {17,80,66,68}, {}, {}, {58,88}, 4},
153--Rot Worm
154[360] = {3, 17, 2, 5, 2, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, {}, {}, {}, {}, 0},
155--Scorpion, Giant
156[426] = {13, 13, 3, 20, 8, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, {}, {}, {}, {}, 3},
157--Sea Serpent
158[346] = {17, 17, 9, 70, 30, ALIGNMENT_EVIL, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {34,83,51}, {}, {}, {}, 2},
159--Sheep
160[348] = {5, 10, 4, 6, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {129}, 0},
161--Ship
162[412] = {30, 30, 30, 30, 30, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 30},
163--Silver Serpent
164[413] = {60, 21, 8, 200, 60, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 15},
165--Skeleton
166[368] = {10, 14, 7, 20, 6, ALIGNMENT_EVIL, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, {}, {43,36,37,41,38}, {9,1}, {88}, 0},
167--Slime
168[375] = {6, 6, 2, 15, 4, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
169--Snake
170[358] = {3, 7, 6, 5, 2, ALIGNMENT_CHAOTIC, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 1},
171--Spider, Giant
172[361] = {9, 11, 4, 10, 8, ALIGNMENT_CHAOTIC, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, {89}, {}, {}, {129}, 0},
173--Squid, Giant
174[345] = {24, 20, 8, 50, 20, ALIGNMENT_EVIL, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, {17,68}, {}, {}, {}, 0},
175--Swashbuckler
176[377] = {15, 18, 12, 15, 4, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {43,41,33}, {19,1,9}, {88,88}, 0},
177--Tangle Vine
178[366] = {20, 25, 1, 30, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, {}, {}, {}, {}, 4},
179--Troll
180[371] = {18, 14, 9, 15, 6, ALIGNMENT_EVIL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {37,36,43,41,34}, {9,18,1,19}, {98,88,88}, 4},
181--Villager
182[379] = {11, 12, 12, 8, 4, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {43,33}, {17}, {88}, 0},
183--Wisp
184[373] = {1, 40, 20, 40, 1, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, {20,69,129}, {}, {}, {}, 0},
185--Wolf
186[351] = {8, 16, 8, 8, 4, ALIGNMENT_CHAOTIC, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
187--TV Flower
188[365] = {20, 5, 1, 30, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, {20}, {}, {}, {}, 4},
189--Skiff
190[414] = {20, 20, 20, 20, 20, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 20},
191--Raft
192[415] = {10, 10, 10, 10, 10, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 10},
193--Balloon
194[423] = {10, 10, 10, 10, 10, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 10},
195--Cat
196[388] = {4, 18, 8, 6, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
197--Sinjen (Yew Stocks guy)
198[263] = {11, 12, 12, 8, 4, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0}
199}
200
201monster_inv_food = { 129, 133, 128 } --portion of meat, ham, loaf of bread
202
203function actor_put_to_sleep(actor)
204	if actor.obj_n == 431 then --horse with rider
205		Actor.use(actor) --dismount from horse
206	end
207
208	if actor.actor_num ~= 0 then
209		actor.asleep = true
210		if actor.in_party == true then
211			party_update_leader()
212		end
213	end
214end
215
216function actor_can_turn_invisible(obj_n)
217
218	if obj_n < 0x1aa and obj_n ~= 0x19b and obj_n ~= 0x176 and obj_n ~= 0x19d and obj_n ~= 0x177 then --giant scorpion, dragon, hydra, silver serpent, slime
219		return true
220	end
221
222	return false
223end
224
225-- returns true if defender loses the check
226function actor_int_check(defender, attacker)
227
228   if is_god_mode_enabled() then
229      if defender.in_party then
230        return true
231      end
232      if attacker.in_party then
233        return false
234      end
235   end
236   if (math.floor(actor_int_adj(attacker) / 2) + 15) - actor_int_adj(defender) > math.random(1, 30) then
237      return true -- defender lost
238   end
239
240   return false
241-- if((actor1_int_adj / 2 + 15) - actor2_int_adj > random(1,30))
242--return
243
244end
245
246function subtract_movement_pts(actor, pts)
247   if actor.obj_n == 0x1af then pts = math.floor(pts / 2) end --if horse with rider
248
249   --fixme check with the original there are two timers that are checked but never seem to be used.
250
251   if pts < 1 then pts = 1 end
252
253   actor.mpts = actor.mpts - pts
254
255   if g_avatar_died == true then
256      actor_avatar_death()
257   end
258end
259
260function actor_move(actor, direction, flag)
261   ----dgb("actor_move("..actor.name..", "..direction_string(direction)..", "..flag..") actor("..actor.x..","..actor.y..")\n");
262   local x,y,z = actor.x, actor.y, actor.z
263   if direction == DIR_NORTH then y = y - 1 end
264   if direction == DIR_SOUTH then y = y + 1 end
265   if direction == DIR_EAST then x = x + 1 end
266   if direction == DIR_WEST then x = x - 1 end
267
268   actor.direction = direction
269   local did_move = Actor.move(actor, x, y, z)
270
271   --actor.direction = direction
272
273   if did_move then
274      if actor.obj_n == 0x177 then slime_update_frames() end
275      subtract_map_movement_pts(actor)
276      ----dgb("actor_move() did move actor("..actor.x..","..actor.y..")\n");
277   end
278
279   return did_move and 1 or 0
280end
281
282function actor_move_diagonal(actor, x_direction, y_direction)
283   local x,y,z = actor.x, actor.y, actor.z
284   local direction
285
286   if y_direction == DIR_NORTH then
287      y = y - 1
288      direction = x_direction == DIR_EAST and DIR_NORTHEAST or DIR_NORTHWEST
289   end
290   if y_direction == DIR_SOUTH then
291      y = y + 1
292      direction = x_direction == DIR_EAST and DIR_SOUTHEAST or DIR_SOUTHWEST
293   end
294   if x_direction == DIR_EAST then
295      x = x + 1
296      direction = y_direction == DIR_NORTH and DIR_NORTHEAST or DIR_SOUTHEAST
297   end
298   if x_direction == DIR_WEST then
299      x = x - 1
300      direction = y_direction == DIR_NORTH and DIR_NORTHWEST or DIR_SOUTHWEST
301   end
302
303   ----dgb("actor_move_diagonal("..actor.name..", "..direction_string(direction)..")\n");
304   actor.direction = y_direction
305   local did_move = Actor.move(actor, x, y, z)
306
307   if did_move then
308      ----dgb("did move\n");
309      if actor.obj_n == 0x177 then slime_update_frames() end
310      subtract_map_movement_pts(actor)
311
312      --dgb("set dir = "..direction_string(direction).." y_dir ="..direction_string(y_direction).." ")
313   end
314
315   return did_move and 1 or 0
316end
317
318function actor_map_dmg(actor, map_x, map_y, map_z)
319	local obj_n = actor.obj_n
320	local actor_type = actor_tbl[obj_n]
321
322	if actor.alive == false or actor.hit_flag == true then
323		return
324	end
325
326	if obj_n ~= 0x1a7 and obj_n ~= 0x19e and obj_n ~= 0x19f and obj_n ~= 0x19c --balloon, skiff, raft, ship, immune to map dmg
327	   and (actor_type == nil or actor_type[13] == 0) and actor.protected == false and actor.in_vehicle == false then
328		local map_tile = map_get_dmg_tile_num(map_x, map_y, map_z)
329		if map_tile ~= nil then
330			actor_tile_dmg(actor, map_tile)
331
332			if map_tile == 732 then
333				local trap_obj = map_get_obj(map_x, map_y, map_z, 0xad) --trap
334				if trap_obj ~= nil then
335					trap_obj.invisible = false
336				end
337			end
338		end
339	end
340end
341
342function actor_tile_dmg(actor, map_tile)
343		if is_god_mode_enabled() and actor.in_party then
344			return
345		end
346		local obj_n = actor.obj_n
347		local actor_type = actor_tbl[actor.obj_n]
348		local dmg = 0
349		local random = math.random
350		if map_tile ~= nil then
351			actor.hit_flag = true
352			if map_tile == 564 then
353				--web
354				if obj_n ~= 0x169 then --giant spider
355					local str = actor_str_adj(actor)
356					if str < random(1, 0x1e) then
357						actor.paralyzed = true
358						print("`"..actor.name.." is stuck in a web!\n")
359					end
360				end
361			elseif (map_tile >= 2 and map_tile <= 5) or map_tile == 1165 then --swamp tiles or poison field.
362				--poison
363				local swamp_boots = Actor.inv_get_readied_obj_n(actor, 7) == 0x1c --swamp boots
364				if swamp_boots == false or map_tile == 1165 then
365					if (actor_type == nil or actor_type[19] == 0) and actor.poisoned == false then --19 immune to poison
366						actor.poisoned = true
367						print(actor.name.." poisoned!\n")
368						hit_anim(actor.x, actor.y)
369					end
370				end
371			elseif (map_tile >= 220 and map_tile <= 223) or map_tile == 890 or map_tile == 1124 or map_tile == 1125 or map_tile == 1130 or map_tile == 1131 or map_tile == 1164 or map_tile == 1193 then
372				--fire
373				if map_tile == 1164 then
374					dmg = random(1, 0x1e)
375				else
376					dmg = random(1, 8)
377				end
378				if actor_type ~= nil and actor_type[17] == 1 then --double damage from fire
379					dmg = dmg * 2
380				end
381
382				actor_hit(actor, dmg, nil)
383
384			elseif map_tile == 732 or map_tile == 1010 then
385				--trap, log saw
386
387				actor_hit(actor, random(1, 0x1e), nil)
388
389			elseif (map_tile >= 1458 and map_tile <= 1463) then
390				if obj_n ~= 0x16e then --tangle vine
391					actor_hit(actor, random(1, 8), nil)
392				end
393			elseif map_tile == 562 or map_tile == 731 then
394				--force field, spikes
395				actor_hit(actor, random(1, 8), nil)
396
397			elseif map_tile == 1167 then
398				--sleepfield
399				if (actor_type == nil or actor_type[21] == 0) and actor.asleep == false then --21 immune to sleep spell
400					actor_put_to_sleep(actor)
401				end
402			end
403		end
404
405end
406
407function actor_get_max_magic_points(actor)
408   local obj_n = actor.base_obj_n
409   if obj_n == 410 then --avatar
410      return actor.int * 2
411   end
412
413   if obj_n == 378 then --mage
414      return actor.int
415   end
416
417   if obj_n == 377 or obj_n == 386 then --swashbuckler, musician
418      return math.floor(actor.int/2)
419   end
420
421   return 0
422end
423
424function actor_str_adj(actor)
425
426   local str = actor.str
427
428   if actor.cursed == false then return str end
429
430   if str <= 3 then
431      return 1
432   end
433
434   return str - 3
435end
436
437function actor_dex_adj(actor)
438
439   local dex = actor.dex
440   if actor.cursed == true then
441      if dex <= 3 then
442         dex = 1
443      else
444         dex = dex - 3
445      end
446   end
447
448   if g_time_stopped == true or actor.asleep == true then
449     dex = 1
450   end
451
452   return dex
453end
454
455function actor_int_adj(actor)
456   local int = actor.int
457
458   if actor.cursed == true then
459     int = int - 3
460   end
461
462   if int < 1 then int = 1 end
463
464   return int
465end
466
467--
468-- actor_combat_hit_check(attacker, foe, weapon_obj)
469--
470
471function actor_combat_hit_check(attacker, foe, weapon_obj)
472
473   if foe == nil then return false end
474	if is_god_mode_enabled() and attacker.in_party then
475		return true
476	end
477	if foe.luatype ~= "actor" or weapon_obj.obj_n == 48 then --48 glass sword
478      --dgb(foe.luatype)
479		return true
480	end
481
482	local use_str = false
483	local obj_n = weapon_obj.obj_n
484
485	if weapon_obj == attacker then
486		if actor_tbl[obj_n][16] == 1 then --actor uses strength
487			use_str = true
488		end
489	else
490		if obj_n == 4 or obj_n == 34 or obj_n == 39 or obj_n == 44 then --spiked helm, club, mace, two-handed hammer
491			use_str = true
492		end
493	end
494
495   local attack_role
496
497   if use_str then
498      attack_role = actor_str_adj(attacker)
499   else
500      attack_role = actor_dex_adj(attacker)
501   end
502
503   local foe_dex = actor_dex_adj(foe)
504   local roll = math.random(1,30)
505
506   --dgb("foe_dex = "..foe_dex.." attack_role = "..attack_role.." random(1,30) = "..roll.."\n\n")
507
508   if math.floor((foe_dex + 30 - attack_role) / 2) >= roll then
509      return false
510   end
511
512   return true
513end
514
515--
516-- actor_init(actor)
517--
518
519function actor_init(actor, alignment)
520
521 local actor_base = actor_tbl[actor.obj_n]
522 if actor_base ~= nil then
523
524   actor.str = actor_randomise_stat(actor_base[1])
525   actor.dex = actor_randomise_stat(actor_base[2])
526   actor.int = actor_randomise_stat(actor_base[3])
527   local hp = actor_randomise_stat(actor_base[4])
528   actor.hp = hp
529   actor.level = math.floor((hp + 29) / 30)
530   actor.align = actor_base[6]
531 else
532   actor.str = 10
533   actor.dex = 10
534   actor.int = 10
535   actor.hp = 30
536   actor.level = 1
537   --actor.status = 32
538 end
539
540   if alignment ~= nil and alignment ~= ALIGNMENT_DEFAULT then
541      actor.align = alignment
542   end
543
544   actor.wt = 8
545   actor.combat_mode = 8
546   actor.magic = actor_get_max_magic_points(actor)
547   actor.mpts = actor.dex
548   actor.exp = 100
549
550	if actor.obj_n == 0x16d then --tangle vine pod. Add tangle vines.
551		local actor_x = actor.x
552		local actor_y = actor.y
553		local actor_z = actor.z
554		local a
555
556    	if map_can_put(actor_x + 1, actor_y, actor_z) then
557    		a = Actor.new(0x16e, actor_x + 1, actor_y, actor_z, actor.align, WT_TANGLE)
558    		if a ~= nil then a.direction = DIR_EAST end
559    	end
560     	if map_can_put(actor_x - 1, actor_y, actor_z) then
561     		a = Actor.new(0x16e, actor_x - 1, actor_y, actor_z, actor.align, WT_TANGLE)
562     		if a ~= nil then a.direction = DIR_WEST end
563     	end
564     	if map_can_put(actor_x, actor_y + 1, actor_z) then
565     		a = Actor.new(0x16e, actor_x, actor_y + 1, actor_z, actor.align, WT_TANGLE)
566     		if a ~= nil then a.direction = DIR_SOUTH end
567     	end
568		if map_can_put(actor_x, actor_y - 1, actor_z) then
569     	   	a = Actor.new(0x16e, actor_x, actor_y - 1, actor_z, actor.align, WT_TANGLE)
570     	   	if a ~= nil then a.direction = DIR_NORTH end
571     	end
572	end
573
574 if actor_base ~= nil then
575
576   local obj,i,v,j,qty,chance,chest
577
578   --add spells
579   for i,v in ipairs(actor_base[22]) do
580        obj = Obj.new(336) --charge
581        obj.quality = v
582        obj.qty = 1
583        obj.status = 56
584        Actor.inv_add_obj(actor, obj)
585   end
586
587   --actor weapon
588   chance = 2
589   for i,v in ipairs(actor_base[23]) do
590      if v >= 36 and v <= 38 then --spear, throwing axe, dagger
591         qty = math.random(6, 12)
592      else
593         qty = 1
594      end
595
596      for j=1,qty do
597         obj = Obj.new(v)
598         obj.status = 57
599         if v == 90 then
600            obj.qty = 1
601         else
602            obj.qty = 0
603         end
604         Actor.inv_add_obj(actor, obj)
605         Actor.inv_ready_obj(actor, obj)
606         if v == 41 or v == 54 then --bow, magic bow
607            obj = Obj.new(55) --arrow
608            obj.status = 49
609            obj.qty = math.random(12, 24)
610            Actor.inv_add_obj(actor, obj)
611         elseif v == 42 or v == 50 then --crossbow, triple crossbow
612            obj = Obj.new(56) --bolt
613            obj.status = 49
614            obj.qty = math.random(12, 24)
615            Actor.inv_add_obj(actor, obj)
616         end
617      end
618
619      chance = chance * 2
620   end
621
622   -- actor armor
623   chance = 2
624   for i,v in ipairs(actor_base[24]) do
625
626      if math.random(1,chance) == 1 then
627
628        obj = Obj.new(v)
629        obj.status = 57
630        obj.qty = 1
631        obj.status = 56
632        Actor.inv_add_obj(actor, obj)
633        Actor.inv_ready_obj(actor, obj)
634      end
635
636      chance = chance * 2
637   end
638
639   --actor treasure
640   chance = 2
641   for i,v in ipairs(actor_base[25]) do
642
643      if math.random(1,chance) == 1 then
644         if v == 98 then --chest
645            chest = Obj.new(v)
646            chest.frame_n = 1
647            chest.status = 41
648            chest.qty = math.random(1, 100)
649            Actor.inv_add_obj(actor, chest)
650            if math.random(0, 1) == 1 then
651               obj = Obj.new(337) --effect
652               obj.status = 41
653               obj.qty = 1
654               obj.quality = 22
655               Obj.moveToCont(obj, chest)
656            end
657            if math.random(0, 3) ~= 0 then
658               obj = Obj.new(88) --gold coin
659               obj.status = 41
660               obj.qty = math.random(1, 100)
661               Obj.moveToCont(obj, chest)
662            end
663            if math.random(0, 3) ~= 0 then
664               obj = Obj.new(monster_inv_food[math.random(1, 3)])
665               obj.status = 41
666               obj.qty = math.random(1, 10)
667               Obj.moveToCont(obj, chest)
668            end
669            if math.random(0, 3) == 0 then
670               obj = Obj.new(90) --torch
671               obj.status = 41
672               obj.qty = math.random(1, 6)
673               Obj.moveToCont(obj, chest)
674            end
675            if math.random(0, 10) == 0 then
676               obj = Obj.new(77) --gem
677               obj.status = 41
678               obj.qty = math.random(1, 4)
679               Obj.moveToCont(obj, chest)
680            end
681         elseif v == 58 then --spell
682            if math.random(0, 3) ~= 0 and actor.obj_n ~= 362 then --Gargoyle
683               obj = Obj.new(88)
684               obj.status = 49
685               obj.qty = math.random(1, 100)
686               Actor.inv_add_obj(actor, obj)
687            end
688            if math.random(0, 3) == 0 then
689               obj = Obj.new(monster_inv_food[math.random(1, 3)])
690               obj.status = 49
691               obj.qty = math.random(1, 100)
692               Actor.inv_add_obj(actor, obj)
693            end
694            if math.random(0, 3) == 0 then
695               obj = Obj.new(275) --potion
696               obj.frame_n = math.random(0, 7) --random potion type
697               obj.status = 49
698               obj.qty = 1
699               Actor.inv_add_obj(actor, obj)
700            end
701         elseif v == 88 then --gold coin
702            obj = Obj.new(v)
703            obj.status = 49
704            obj.qty = math.random(1,30) + math.random(1, 30)
705            Actor.inv_add_obj(actor, obj)
706         else
707            obj = Obj.new(v)
708            obj.quality = 49
709            obj.qty = 1
710            Actor.inv_add_obj(actor, obj)
711         end
712
713      end
714
715      chance = chance * 2
716   end
717 end
718end
719
720-- [objectnum] = range
721   g_range_weapon_tbl = {
722      [40] = 2, -- morning star
723      [47] = 2, -- halberd
724      [38] = 3, -- dagger
725      [83] = 5, -- flask of oil
726      [33] = 4, -- sling
727      [37] = 3, -- throwing axe
728      [36] = 4, -- spear
729      [41] = 5, -- bow
730      [42] = 7, -- crossbow
731      [57] = 7, -- spellbook
732      [336] = 5, -- charge
733      [49] = 5, -- boomerang
734      [50] = 7, -- triple crossbow
735      [412] = 5, -- ship
736      [91] = 4, -- Zu Ylem
737      [54] = 7, -- magic bow
738      [79] = 7, -- lightning wand
739      [80] = 7, -- fire wand
740   }
741
742local projectile_weapon_tbl =
743{
744--obj_n = {tile_num, initial_tile_rotation, speed, rotation_amount}
745[33] = {398, 0, 2, 0}, -- sling
746[36] = {547, 45,3, 0}, -- spear
747[37] = {548, 0, 2, 10}, -- throwing axe
748[38] = {549, 0, 2, 10}, -- dagger
749[41] = {566, 90,4, 0}, -- bow
750[42] = {567, 90,4, 0}, -- crossbow
751[49] = {560, 0, 4, 10}, -- boomerang
752[83] = {601, 0, 2, 0}, -- flask of oil
753[50] = {567, 90,4, 0}, -- triple crossbow
754[412] = {399, 0,2, 0}, -- ship
755[91] = {612, 0, 2, 10}, -- Zu Ylem
756[54] = {566, 90,4, 0}, -- magic bow
757[79] = {392, 0, 2, 0}, -- lightning wand
758[80] = {393, 0, 2, 0}, -- fire wand
759}
760
761weapon_dmg_tbl = {
762[4] = 4, --spiked helm
763[33] = 6, --sling
764[34] = 8, --club
765[35] = 8, --main gauche
766[36] = 10, --spear
767[37] = 10, --throwing axe
768[38] = 6, --dagger
769[39] = 15, --mace
770[40] = 15, --morning star
771[41] = 10, --bow
772[42] = 12, --crossbow
773[43] = 15, --sword
774[44] = 20, --two-handed hammer
775[45] = 20, --two-handed axe
776[46] = 20, --two-handed sword
777[47] = 30, --halberd
778[49] = 8, --boomerang
779[50] = 12, --triple crossbow
780[48] = 255, --glass sword
781[13] = 4, --spiked shield
782[221] = 90, --cannon
783[83] = 4, --flask of oil
784[54] = 20, --magic bow
785[109] = 2, --rolling pin
786[100] = 6, --scythe
787[101] = 4, --pitchfork
788[103] = 4, --pick
789[104] = 4, --shovel
790[105] = 4, --hoe
791[113] = 4, --cleaver
792[114] = 4, --knife
793[141] = 4, --decorative sword
794[412] = 30, --ship
795[91] = 0, --Zu Ylem
796[78] = 4, --staff
797[79] = 30, --lightning wand
798[80] = 20, --fire wand
799}
800
801armour_tbl =
802{
803[1] = 1, --leather helm
804[2] = 2, --chain coif
805[3] = 3, --iron helm
806[4] = 3, --spiked helm
807[5] = 2, --winged helm
808[6] = 2, --brass helm
809[7] = 3, --Gargoyle Helm
810[8] = 5, --magic helm
811[9] = 2, --wooden shield
812[10] = 3, --curved heater
813[11] = 3, --winged shield
814[12] = 3, --kite shield
815[13] = 2, --spiked shield
816[14] = 2, --black shield
817[15] = 4, --door shield
818[16] = 5, --magic shield
819[17] = 1, --cloth armour
820[18] = 2, --leather armor
821[19] = 3, --ring mail
822[20] = 4, --scale mail
823[21] = 5, --chain mail
824[22] = 7, --plate mail
825[23] = 10, --magic armour
826[35] = 1, --main gauche
827[24] = 2, --spiked collar
828[256] = 5, --protection ring
829}
830
831function get_weapon_dmg(weapon_obj_n)
832   local dmg = weapon_dmg_tbl[weapon_obj_n]
833
834   if dmg == nil and actor_tbl[weapon_obj_n] ~= nil then
835      dmg = actor_tbl[weapon_obj_n][5]
836   end
837
838   return dmg
839end
840
841function actor_get_ac(actor)
842
843   local ac = 0
844   local obj
845
846   for obj in actor_inventory(actor) do
847      if obj.readied then
848
849         local armour = armour_tbl[obj.obj_n]
850         if armour ~= nil then
851            ac = ac + armour
852         end
853      end
854   end
855
856   if actor.cursed == true then
857      ac = ac - 3
858   end
859
860   if actor.protected == true then
861      ac = ac + 3
862   end
863
864   return ac
865end
866
867function actor_select_obj_from_tbl(actor, obj_list_tbl)
868	local obj
869	local selected_obj = nil
870	local random = math.random
871	for obj in actor_inventory(actor) do
872		if obj_list_tbl[obj.obj_n] ~= nil then
873			if selected_obj == nil or random(0, 1) == 0 then
874				selected_obj = obj
875			end
876		end
877	end
878
879	return selected_obj
880end
881
882function acid_slug_dissolve_item(target_actor)
883	local acid_slug_items = { [0x2] = 1,
884	[0x3] = 1,
885	[0x4] = 1,
886	[0x5] = 1,
887	[0x7] = 1,
888	[0x0A] = 1,
889	[0x0B] = 1,
890	[0x0C] = 1,
891	[0x0D] = 1,
892	[0x0F] = 1,
893	[0x13] = 1,
894	[0x14] = 1,
895	[0x15] = 1,
896	[0x16] = 1,
897	[0x23] = 1,
898	[0x26] = 1,
899	[0x2B] = 1,
900	[0x71] = 1,
901	[0x72] = 1 }
902
903	local obj = actor_select_obj_from_tbl(target_actor, acid_slug_items)
904
905	if obj ~= nil then
906		play_sfx(SFX_SLUG_DISSOLVE, true)
907		print("A slug dissolves "..target_actor.name.."'s "..obj.name.."!\n")
908		Actor.inv_remove_obj(target_actor, obj)
909		obj = nil
910	end
911end
912
913function gremlin_steal_item(target_actor)
914	local gremlin_items = { [0x80] = 1,
915	[0x81] = 1,
916	[0x82] = 1,
917	[0x83] = 1,
918	[0x84] = 1,
919	[0x85] = 1,
920	[0x87] = 1,
921	[0xD1] = 1,
922	[0xD2] = 1,
923	[0xD3] = 1 }
924
925	local obj = actor_select_obj_from_tbl(target_actor, gremlin_items)
926
927	if obj ~= nil then
928		play_sfx(SFX_FAILURE, true)
929		print("`"..target_actor.name.."  has been robbed!\n")
930		Actor.inv_remove_obj(target_actor, obj)
931		obj = nil
932	end
933end
934
935function actor_take_hit(attacker, defender, max_dmg)
936   if attacker.obj_n == 357 then --corpser
937      attacker.frame_n = 1 --reveal corpser.
938   end
939   if max_dmg == -1 then
940      max_dmg = 1
941   elseif max_dmg > 1 and max_dmg ~= 255 then
942      max_dmg = math.random(1, max_dmg)
943   end
944
945   local ac
946   local defender_type = defender.luatype
947   if defender_type == "actor" then
948      ac = actor_get_ac(defender)
949      if max_dmg ~= 255 and ac > 0 then
950         max_dmg = max_dmg - math.random(1, ac)
951      end
952      --dgb("\nac = "..ac.."\n")
953   else
954      ac = 0 --object
955   end
956   if is_god_mode_enabled() then
957     if defender_type == "actor" and defender.in_party then
958--        print("`"..defender.name.." grazed.\n")
959        return
960     elseif attacker.in_party then
961        max_dmg = 255
962     end
963   end
964   if max_dmg > 0 then
965      if defender_type == "actor" and defender.wt > 1 and defender.wt < 16 then
966         --FIXME defender now targets attacker. I think.
967      end
968
969      local exp_gained = actor_hit(defender, max_dmg, attacker)
970
971      attacker.exp = attacker.exp + exp_gained
972   else
973      print("`"..defender.name.." grazed.\n")
974   end
975
976   if defender_type == "actor" then
977
978      actor_yell_for_help(attacker, defender, max_dmg)
979
980      local defender_obj_n = defender.obj_n
981
982      if defender.alive == true
983        or defender_obj_n == 0x1a7 --balloon
984        or defender_obj_n == 0x19e --skiff
985        or defender_obj_n == 0x19f --raft
986        or defender_obj_n == 0x19c then --ship
987
988         local attacker_obj_n = attacker.obj_n
989
990         if attacker_obj_n == 0x165 then --corpser
991         	play_sfx(SFX_CORPSER_DRAGGED_UNDER, true)
992            print("`"..defender.name.." dragged under!\n")
993            defender.corpser_flag = true
994            if defender.in_party == true then
995               party_update_leader()
996            end
997         end
998
999         if attacker_obj_n == 0x16c then --acid slug
1000         	acid_slug_dissolve_item(defender)
1001         end
1002
1003         if attacker_obj_n == 0x161 then --gremlin
1004         	gremlin_steal_item(defender)
1005         end
1006
1007         local actor_type = actor_tbl[attacker_obj_n]
1008         if actor_type ~= nil and actor_type[15] == 1 and math.random(0, 3) == 0 and defender.actor_num ~= 0 then --actor is poisonous, don't poison vehicles.
1009         	defender.poisoned = true
1010         	print("`"..defender.name.." poisoned!\n")
1011         end
1012
1013         if max_dmg > 0 then
1014         	actor_hit_msg(defender)
1015         end
1016      else
1017         print("`"..defender.name.." killed!\n")
1018         --FIXME do party roster change. maybe
1019      end
1020
1021   end
1022end
1023
1024function actor_hit(defender, max_dmg, attacker, no_hit_anim)
1025
1026	local defender_obj_n = defender.obj_n
1027	local exp_gained = 0
1028
1029	if defender_obj_n == 0x1a7 then -- balloon.
1030		return 0
1031	end
1032
1033	if defender.luatype == "actor" then
1034		if is_god_mode_enabled() then
1035			if defender.in_party then
1036				return 0
1037			else
1038				max_dmg = 255
1039			end
1040		end
1041		--actor logic here
1042		defender.hit_flag = true
1043		if no_hit_anim == nil then
1044			hit_anim(defender.x, defender.y)
1045		end
1046		if defender_obj_n == 409 then --lord british
1047			defender.hp = 0xff
1048		elseif defender_obj_n == 414 or defender_obj_n == 415 then --skiff, raft
1049			-- hit random party member.
1050			local num_in_party = party_get_size()
1051			local party_member_num = 0
1052			if num_in_party > 1 then
1053				party_member_num = math.random(0, num_in_party - 1)
1054			end
1055
1056			local rand_party_member = party_get_member(party_member_num)
1057			actor_hit(rand_party_member, max_dmg, attacker, true)
1058			actor_hit_msg(rand_party_member)
1059		else
1060			-- actor hit logic here
1061			if max_dmg >= defender.hp then
1062
1063				if defender.hp ~= 0 then
1064					if defender.in_party == true then player_subtract_karma(5) end --allowing your party to die is bad karma.
1065					defender.hp = 0
1066--					defender.alive = false -- This doesn't work. The variable is get only
1067					if alignment_is_evil(defender.align) then
1068						local actor_base = actor_tbl[defender.obj_n]
1069						if actor_base ~= nil then
1070							--exp = (base_str + base_dex + base_int + base_hp + base_dmg + base_exp_gain) / 4
1071							exp_gained = math.floor((actor_base[1] + actor_base[2] + actor_base[3] + actor_base[4] + actor_base[5] + actor_base[26]) / 4)
1072						end
1073					end
1074
1075					defender.visible = true
1076
1077					if defender.obj_n == 412 and defender.actor_num == 0 then --handle ship destruction
1078						Actor.unlink_surrounding_objs(defender, true) --unlink ship front/back and make objects temporary.
1079						defender.base_obj_n = 0x19f --raft
1080						--try to toss raft out from center of the ship.
1081						if actor_move(defender, DIR_NORTH, 1) == 0 then
1082							if actor_move(defender, DIR_SOUTH, 1) == 0 then
1083								if actor_move(defender, DIR_EAST, 1) == 0 then
1084									actor_move(defender, DIR_WEST, 1)
1085								end
1086							end
1087						end
1088						defender.hp = 10
1089						--defender.mpts = 1
1090						party_move(defender.x, defender.y, defender.z)
1091					else
1092						actor_dead(defender)
1093
1094						defender.wt = WT_NOTHING
1095						if defender.in_party == true and defender.in_vehicle == false then
1096							party_update_leader()
1097						end
1098					end
1099				end
1100			else
1101				defender.hp = defender.hp - max_dmg
1102				if defender.wt == WT_FLEE then --flee
1103					defender.wt = WT_ASSAULT --assault
1104				end
1105				if defender_obj_n == 375 then --slime
1106					slime_divide(defender)
1107				end
1108			end
1109		end
1110	else
1111		--object logic here
1112		if defender_obj_n == 0x16e then -- tangle vine
1113			--FIXME do something with tangle vine. -- I think the message is all that needed added
1114			print("\nTangle vine killed!\n") -- The pod doesn't have this text in the original for some reason
1115		elseif defender.stackable == false and defender.qty ~= 0 then
1116			hit_anim(defender.x, defender.y)
1117			if (defender_obj_n < 0x129 or defender_obj_n > 0x12c or defender.frame_n < 0xc) --check frame_n for door objects
1118				and (defender_obj_n ~= 0x62 or defender.frame_n ~= 3) then --don't attack open chests
1119				if defender.qty <= max_dmg then
1120					print("\n`"..defender.name .. " broken!\n")
1121
1122					local child
1123					for child in container_objs(defender) do  -- look through container for effect object.
1124					  if child.obj_n == 0x151 then --effect
1125					  	if attacker ~= nil then
1126					  	  actor_use_effect(attacker, child)
1127					  	end
1128					  	break
1129					  else
1130					  	Obj.moveToMap(child, defender.x, defender.y, defender.z)
1131					  end
1132					end
1133
1134					if defender_obj_n == 0x7b then --mirror
1135						play_sfx(SFX_BROKEN_GLASS)
1136						defender.frame_n = 2
1137						player_subtract_karma(10)
1138					else
1139						map_remove_obj(defender)
1140					end
1141				else
1142					defender.qty = defender.qty - max_dmg
1143				end
1144			end
1145		end
1146	end
1147
1148	return exp_gained
1149end
1150
1151function actor_hit_msg(actor)
1152
1153	if actor.obj_n == 0x1a7 then return end --balloon
1154
1155	local hp = actor.hp
1156
1157	if hp == 0 then return end
1158
1159	local di = math.floor((hp * 4) / actor.max_hp)
1160
1161	local s
1162
1163	if di < 4 then
1164		s = "\n`"..actor.name.." "
1165	end
1166
1167	if di == 0 then
1168		s = s.."critical!\n"
1169		local wt = actor.wt
1170		if actor_int_adj(actor) >= 5 and wt ~= WT_BERSERK and wt ~= WT_STATIONARY and wt > WT_FRONT then
1171			actor.wt = WT_RETREAT
1172		end
1173	elseif di < 4 then
1174
1175		if di == 1 then
1176			s = s.."heavily "
1177		elseif di == 2 then
1178			s = s.."lightly "
1179		elseif di == 3 then
1180			s = s.."barely "
1181		end
1182		s = s.."wounded.\n"
1183	end
1184
1185	if s ~= nil then
1186		print(s)
1187	end
1188end
1189
1190function actor_dead(actor)
1191	local actor_base = actor_tbl[actor.obj_n]
1192	if actor_base ~= nil then
1193		if actor.obj_n == 0x163 then --gazer
1194			Actor.new(0x157, actor.x, actor.y, actor.z) --insect
1195		end
1196
1197		if actor_base[9] == 1 then --fades away. magical creature.
1198			Actor.fade_out(actor, 20) --FIXME make fade speed configurable.
1199		end
1200	end
1201
1202	local in_vehicle = actor.in_vehicle
1203
1204	if in_vehicle == false and actor_base ~= nil and actor_base[8] == 1 or actor.obj_n == 0x187 or actor.obj_n == 0x188 then --farmer, musician
1205		--add some blood.
1206		--dgb("\nAdding Blood\n")
1207		local blood_obj = Obj.new(0x152, math.random(0,2))
1208		blood_obj.temporary = true
1209		Obj.moveToMap(blood_obj, actor.x, actor.y, actor.z)
1210	end
1211
1212	if actor.actor_num ~= 1 then --avatar
1213		local create_body = true
1214		if in_vehicle == true then
1215			create_body = false -- don't drop body when party member dies on raft.
1216		end
1217
1218		if actor_is_readiable_obj(actor) then
1219			local animated_obj = Obj.new(actor.obj_n)
1220			Obj.moveToMap(animated_obj, actor.x, actor.y, actor.z)
1221		end
1222		if actor.obj_n == 431 then --horse with rider
1223			Actor.new(430, actor.x, actor.y, actor.z, ALIGNMENT_NEUTRAL, 12) --horse, wt wander
1224		end
1225		Actor.kill(actor, create_body)
1226	else
1227		actor.hp = 0 -- hackish way of making the Avatar not extinguish his torch
1228		g_avatar_died = true
1229	end
1230
1231	if actor.obj_n == 0x177 then
1232		slime_update_frames()
1233	end
1234end
1235
1236function slime_divide(slime)
1237
1238	local random = math.random
1239	local from_x, from_y, from_z = slime.x, slime.y, slime.z
1240	local i
1241	for i=1,8 do
1242
1243		local new_x = random(1, 2) + from_x - 1
1244		local new_y = random(1, 2) + from_y - 1
1245
1246		if map_can_put(new_x, new_y, from_z) then
1247			Actor.clone(slime, new_x, new_y, from_z)
1248			slime_update_frames()
1249			print("Slime divides!\n")
1250			return
1251		end
1252	end
1253end
1254--[[
1255function bitor(x, y)
1256 local p = 1
1257  while p < x do p = p + p end
1258  while p < y do p = p + p end
1259  local z = 0
1260  repeat
1261   if p <= x or p <= y then
1262     z = z + p
1263     if p <= x then
1264     x = x - p
1265     end
1266     if p <= y then y = y - p end
1267
1268   end
1269  p = p * 0.5
1270  until p < 1
1271  return z
1272end
1273--]]
1274function slime_update_frames()
1275
1276	local i, j
1277	local pow = math.pow
1278
1279	for i=1,0xff do
1280		local actor = Actor.get(i)
1281		if actor.obj_n == 0x177 and actor.alive then --slime
1282			local new_frame_n = 0;
1283			local idx = 0
1284			for j = 1,8,2 do
1285				local tmp_actor = map_get_actor(actor.x + movement_offset_x_tbl[j], actor.y + movement_offset_y_tbl[j], actor.z)
1286				if tmp_actor ~= nil and tmp_actor.obj_n == 0x177 and tmp_actor.alive then
1287					new_frame_n = new_frame_n + pow(2,idx)
1288				end
1289				idx = idx + 1
1290			end
1291			actor.frame_n = new_frame_n
1292		end
1293	end
1294end
1295
1296function combat_range_check_target(actor_attacking)
1297
1298   if actor_attacking.wt > 1 and actor_attacking.wt < 0x10 then
1299--[[ FIXME
1300      target = actor_attacking.target_obj
1301      if target ~= actor_attacking then
1302
1303         if target.obj_n ~= 0 and target.alive == true and target.asleep == false and target.paralysed == false and target.corpser_flag == false then
1304
1305            if abs(actor_attacking.x - target.x) < 2 then
1306
1307               if abs(actor_attacking.y - target.y) < 2 and (time_stop_spell_timer == 0 or target.in_party == true) then
1308
1309                  return false
1310               end
1311            end
1312         end
1313      end
1314--]]
1315   end
1316
1317return true
1318end
1319
1320--check for arrows or bolts if attacking with a bow or crossbow
1321function out_of_ammo(attacker, weapon, print_message)
1322
1323   local weapon_obj_n = weapon.obj_n
1324
1325   if (weapon_obj_n == 41 or weapon_obj_n == 54) and Actor.inv_has_obj_n(attacker, 55) == false then --bow, magic bow, arrows
1326      if(print_message) then
1327         print("Out of arrows!\n")
1328      end
1329      return true --no arrows, bail out
1330   end
1331   if (weapon_obj_n == 42 or weapon_obj_n == 50) and Actor.inv_has_obj_n(attacker, 56) == false then --crossbow, triple crossbow, bolts
1332      if(print_message) then
1333         print("Out of bolts!\n")
1334      end
1335      return true --no bolts, bail out
1336   end
1337   return false
1338end
1339
1340--
1341-- actor_attack(attacker, target, weapon)
1342--
1343
1344function actor_attack(attacker, target_x, target_y, target_z, weapon, foe)
1345
1346   local random = math.random
1347   local weapon_obj_n = weapon.obj_n
1348   local weapon_quality = weapon.quality
1349
1350	if foe ~= nil and foe.actor_num == attacker.actor_num then
1351		print("\n"..attacker.name.." is try to attack itself. Report me!\n")  -- this shouldn't happen
1352		return
1353	end
1354
1355   if foe == nil then
1356      foe = map_get_obj(target_x, target_y, target_z);
1357   end
1358
1359   --dgb("\nactor_attack()\nrange = " .. get_attack_range(attacker.x,attacker.y, target_x, target_y).." weapon range="..get_weapon_range(weapon_obj_n))
1360   if weapon_obj_n ~= attacker.obj_n then
1361      --dgb("\nweapon = "..weapon.name.."obj_n = "..weapon_obj_n.."\n")
1362   end
1363
1364   if Actor.get_range(attacker, target_x, target_y) > get_weapon_range(weapon_obj_n) then
1365      return
1366   end
1367
1368   --check for arrows or bolts if attacking with a bow or crossbow
1369
1370   if (out_of_ammo(attacker, weapon, false) == true) then
1371      return --no arrows or bolts, bail out
1372   end
1373
1374
1375   local x_diff = target_x - attacker.x
1376   local y_diff = target_y - attacker.y
1377   local attacker_direction
1378
1379   if abs(x_diff) <= abs(y_diff) then
1380     attacker_direction = (y_diff >= 0) and DIR_SOUTH or DIR_NORTH
1381   else
1382     attacker_direction = (x_diff >= 0) and DIR_EAST or DIR_WEST
1383   end
1384
1385   local actor_obj_n = attacker.obj_n
1386   --[[
1387   if actor_obj_n == 0x19c then --ship
1388
1389      if(((*(&objlist_npc_movement_flags + actor_attacking) & 6 xor attacker_direction) & 2) == 0)
1390      {
1391         attacker_direction = attacker_direction xor 2;
1392      }
1393      else
1394      {
1395         attacker_direction = *(objlist_npc_movement_flags + actor_attacking) & 7;
1396      }
1397   end
1398--]]
1399
1400   if attacker.obj_n ~= 412 and actor_obj_n ~= 413 then --don't change direction for ship, silver serpent.
1401      attacker.direction = attacker_direction
1402   end
1403
1404   if actor_obj_n >= 0x170 and actor_obj_n <= 0x174 then -- skeleton, mongbat
1405      local frame_n = attacker.frame_n
1406      attacker.frame_n = (frame_n - (frame_n % 4)) + 2
1407   end
1408
1409
1410   local is_range_weapon = false
1411
1412   if weapon_obj_n ~= 40 and weapon_obj_n ~= 47 then -- morning star, halberd
1413      if abs(x_diff) > 1 or abs(y_diff) > 1 or projectile_weapon_tbl[weapon_obj_n] ~= nil then
1414         is_range_weapon = true
1415         if combat_range_check_target(attacker) == false then return 1 end
1416      end
1417   end
1418
1419   local dmg = get_weapon_dmg(weapon_obj_n)
1420   if dmg == nil then
1421      dmg = 1
1422   end
1423
1424   --FIXME run unknown func sub_1EABC here.
1425   local player_loc = player_get_location()
1426
1427   if weapon_obj_n == 0x150 or weapon_obj_n == 0x39 then --charge, spellbook
1428
1429      if weapon_quality == 0x81 and actor_obj_n == 0x175 then -- special wisp magic (wisp teleport), wisp
1430
1431         if toss_actor(attacker, player_loc.x, player_loc.y, player_loc.z) == true then
1432
1433            print("Wisp teleports!\n")
1434         end
1435
1436         return
1437      end
1438
1439      local spell_retcode = 0
1440
1441      magic_cast_spell(weapon_quality, attacker, {x = target_x, y = target_y, z = target_z})
1442
1443      if weapon_quality == 0x50 and spell_retcode == 0xfe then
1444         print("`"..foe.name .. " is charmed.\n")
1445      elseif weapon_quality == 0x45 and spell_retcode == 0 then
1446         print("`"..foe.name .. " is paralyzed.\n")
1447      end
1448
1449      return
1450   end
1451
1452
1453   --weapon here.
1454
1455   local hit_actor = actor_combat_hit_check(attacker, foe, weapon)
1456   local num_bolts
1457   local missed_target = false
1458
1459   if is_range_weapon == true then
1460      --FIXME might need to get new foe here.
1461      target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
1462
1463     local failed_line_check
1464     if foe ~= nil and (foe.x ~= target_x or foe.y ~= target_y) then
1465        hit_actor = false
1466        failed_line_check = true
1467     else
1468        failed_line_check = false
1469     end
1470
1471      if hit_actor == false and failed_line_check == false then
1472         if foe ~= nil then
1473            target_x = target_x + random(0, 2) - 1
1474            target_y = target_y + random(0, 2) - 1
1475            local new_foe = map_get_actor(target_x, target_y, attacker.z)
1476            if new_foe ~= foe then -- make sure multi-tile actors don't get attacked again on a miss
1477               foe = new_foe
1478               hit_actor = actor_combat_hit_check(attacker, foe, weapon)
1479            end
1480         end
1481      end
1482
1483      if weapon_obj_n == 0x32 then --triple crossbow
1484         num_bolts = Actor.inv_get_obj_total_qty(attacker, 0x38)
1485         --dgb("total num_bolts = "..num_bolts.."\n")
1486         if num_bolts > 3 then num_bolts = 3 end
1487      end
1488
1489      if failed_line_check == false then
1490      --FIXME might need to get new foe here.
1491      target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
1492
1493         if foe ~= nil and (foe.x ~= target_x or foe.y ~= target_y) then
1494            hit_actor = false -- leaving off line check bool since triple bolts should probably do damage
1495         end
1496      end
1497      combat_range_weapon_1D5F9(attacker, target_x, target_y, target_z, foe, weapon)
1498
1499
1500   else --standard weapon
1501      if actor_find_max_xy_distance(attacker, player_loc.x, player_loc.y) < 6 then
1502         --play_sound_effect(0x11, 0);
1503         play_sfx(SFX_ATTACK_SWING, true)
1504      end
1505   end
1506
1507   if weapon_obj_n == 0x32 then --triple crossbow
1508
1509      local off = ((attacker.y - target_y + 5) * 11) + (attacker.x - target_x + 5)
1510      local i
1511      for i=1,num_bolts do
1512
1513         if i > 1 then
1514            --dgb("num_bolts = "..num_bolts.." off = "..off.." target_x = "..target_x.." target_y = "..target_y.."attacker.x = "..attacker.x.." attacker.y = "..attacker.y.."\n\n")
1515            local t = g_projectile_offset_tbl[i-1][off+1]
1516
1517            foe = map_get_actor(target_x + movement_offset_x_tbl[t+1], target_y + movement_offset_y_tbl[t+1], player_loc.z)
1518            --dgb("new_x = "..target_x + movement_offset_x_tbl[t+1].." new_y = "..target_y + movement_offset_y_tbl[t+1].."\n");
1519            if failed_line_check == true then
1520                hit_actor = false
1521            else
1522                target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
1523                if foe ~= nil and foe.x == target_x and foe.y == target_y then
1524                    hit_actor = actor_combat_hit_check(attacker, foe, weapon);
1525                else
1526                    hit_actor = false
1527                end
1528            end
1529         end
1530
1531         if hit_actor == true then
1532            --dgb("triple xbow hit actor dmg = "..dmg.."\n");
1533            actor_take_hit(attacker, foe, dmg);
1534            if g_avatar_died == true then
1535                return
1536            end
1537         end
1538
1539      end
1540   else
1541
1542      if hit_actor == true then
1543
1544         local actor_base = actor_tbl[foe.obj_n]
1545         if actor_base ~= nil and actor_base[14] == 1 -- takes half dmg
1546            and weapon_obj_n ~= 0x30 and weapon_obj_n ~= 0x32 and weapon_obj_n ~= 0x36 then --glass sword, triple crossbow, magic bow
1547            dmg = math.floor((dmg + 1) / 2)
1548         end
1549
1550         if weapon_obj_n ~= 0x5b then --Zu Ylem
1551            actor_take_hit(attacker, foe, dmg)
1552         end
1553
1554         if weapon_obj_n == 0x30 then
1555            print("Thy sword hath shattered!\n")
1556            Actor.inv_remove_obj(attacker, weapon)
1557         end
1558      end
1559
1560      if weapon_obj_n == 0x31 then --boomerang
1561         if attacker.x ~= 0 and attacker.y ~= 0 then --hack to stop return projectile if the avatar has died
1562         	projectile(projectile_weapon_tbl[weapon_obj_n][1], target_x, target_y, attacker.x, attacker.y, projectile_weapon_tbl[weapon_obj_n][3], projectile_weapon_tbl[weapon_obj_n][4])
1563         end
1564      end
1565   end
1566end
1567
1568
1569function combat_range_weapon_1D5F9(attacker, target_x, target_y, target_z, foe, weapon)
1570
1571   local weapon_obj_n = weapon.obj_n
1572   local random = math.random
1573
1574   if weapon_obj_n == 0x32 then --triple cross bow
1575      local index = ((attacker.y - target_y + 5) * 11) + (attacker.x - target_x + 5) + 1
1576      local triple_crossbow_targets = {
1577                      {x=target_x,
1578                       y=target_y,
1579                       z=target_z},
1580                      {x=target_x + movement_offset_x_tbl[g_projectile_offset_tbl[1][index]+1],
1581                       y=target_y + movement_offset_y_tbl[g_projectile_offset_tbl[1][index]+1],
1582                       z=target_z},
1583                      {x=target_x + movement_offset_x_tbl[g_projectile_offset_tbl[2][index]+1],
1584                       y=target_y + movement_offset_y_tbl[g_projectile_offset_tbl[2][index]+1],
1585                       z=target_z}
1586                    }
1587
1588      projectile_anim_multi(projectile_weapon_tbl[weapon_obj_n][1], attacker.x, attacker.y, triple_crossbow_targets, projectile_weapon_tbl[weapon_obj_n][3], 0, projectile_weapon_tbl[weapon_obj_n][2])
1589   else
1590      projectile(projectile_weapon_tbl[weapon_obj_n][1], attacker.x, attacker.y, target_x, target_y, projectile_weapon_tbl[weapon_obj_n][3], projectile_weapon_tbl[weapon_obj_n][4])
1591   end
1592
1593   if weapon_obj_n == 0x5b then
1594      --Zu Ylem
1595      if foe ~= nil and foe.obj_n ~= 0 then
1596         Actor.inv_remove_obj_qty(attacker, 0x5b, 1)
1597         spell_put_actor_to_sleep(attacker, foe)
1598      end
1599   elseif weapon_obj_n == 0x4f or weapon_obj_n == 0x50 then
1600      --lightning wand, fire wand
1601      if random(1, 100) == 37 then
1602      	Actor.inv_remove_obj(attacker, weapon)
1603      	print("A wand has vanished!\n")
1604      end
1605   elseif weapon_obj_n == 0x53 then
1606      --flask of oil
1607
1608      Actor.inv_remove_obj_qty(attacker, 0x53, 1)
1609
1610      if map_is_water(target_x,target_y,target_z) == false then
1611	      local obj = Obj.new(317); --fire field
1612          obj.temporary = true
1613	      Obj.moveToMap(obj, target_x, target_y, target_z)
1614      end
1615
1616   elseif weapon_obj_n == 0x24 or weapon_obj_n == 0x25 or weapon_obj_n == 0x26 then
1617      --spear, throwing axe, dagger
1618
1619      if Actor.inv_remove_obj_qty(attacker, weapon_obj_n, 1) == 1 and map_is_water(target_x,target_y,target_z) == false then
1620	      local obj = Obj.new(weapon_obj_n);
1621	      obj.ok_to_take = true
1622	      obj.temporary = true
1623		  Obj.moveToMap(obj, target_x, target_y, target_z)
1624	  end
1625
1626   elseif weapon_obj_n == 0x29 or weapon_obj_n == 0x2a or weapon_obj_n == 0x32 or weapon_obj_n == 0x36 then
1627      --bow, crossbow, triple crossbow, magic bow
1628      local projectile_obj = nil
1629      if weapon_obj_n == 0x29 or weapon_obj_n == 0x36 then --bow, magic bow
1630      	projectile_obj = 0x37 --arrow
1631      else
1632      	projectile_obj = 0x38 --bolt
1633      end
1634
1635      local qty = 1
1636      if weapon_obj_n == 0x32 then -- triple crossbow
1637      	qty = 3
1638      end
1639
1640      Actor.inv_remove_obj_qty(attacker, projectile_obj, qty)
1641   end
1642
1643   return 1
1644end
1645
1646--
1647-- actor_get_weapon()
1648--
1649function actor_get_weapon(attacker, foe)
1650
1651   if foe == nil then return nil end
1652
1653   local in_party = attacker.in_party
1654   local range = Actor.get_range(attacker, foe.x, foe.y)
1655   --dgb("range = "..range.."\n")
1656   local max_dmg = 0
1657   local obj, weapon
1658
1659   for obj in actor_inventory(attacker) do
1660      if obj.obj_n == 336 and timer_get(TIMER_STORM) == 0 and math.random(0,3) == 0 and math.random(0, obj.quality) < 16 then --charge (spell)
1661         --dgb("magic object quality = "..obj.quality.."\n");
1662         return obj
1663      else
1664         if in_party == false or obj.readied == true then
1665            local dmg = get_weapon_dmg(obj.obj_n)
1666            if dmg ~= nil and dmg > max_dmg and get_weapon_range(obj.obj_n) >= range then
1667               max_dmg = dmg
1668               weapon = obj
1669            end
1670         end
1671      end
1672   end
1673
1674   if weapon == nil then --attack with bare hands.
1675   	weapon = attacker
1676   end
1677
1678   --dgb("weapon: "..weapon.name.." dmg="..get_weapon_dmg(weapon.obj_n).."\n")
1679
1680   return weapon
1681end
1682
1683--
1684-- actor_calculate_avg_coords()
1685--
1686function actor_calculate_avg_coords()
1687   local n = 0
1688   local avg_x = 0
1689   local avg_y = 0
1690
1691   local player_loc = player_get_location()
1692
1693   local player_x = player_loc.x
1694   local player_y = player_loc.y
1695
1696   local player = Actor.get_player_actor()
1697   local player_dir = player.direction
1698
1699   local actor
1700   for actor in party_members() do
1701      if actor.wt ~= WT_FLANK and actor.wt ~= WT_BERSERK then
1702         n = n + 1
1703         avg_x = avg_x + actor.x
1704         avg_y = avg_y + actor.y
1705      end
1706   end
1707
1708   if n > 0 then
1709      party_avg_x = math.floor(avg_x / n)
1710      party_avg_y = math.floor(avg_y / n)
1711   else
1712      party_avg_x = player_x
1713      party_avg_y = player_y
1714   end
1715
1716   n = 0
1717   avg_x = 0
1718   avg_y = 0
1719
1720   local i
1721   for i=0,0xff do
1722      actor = Actor.get(i)
1723
1724      if actor.obj_n ~= 0 and actor.alive then
1725
1726         if (actor.align == ALIGNMENT_EVIL or actor.align == ALIGNMENT_CHAOTIC) then
1727            if actor.wt ~= WT_RETREAT or (abs(actor.x -  player_x) <= 5 and abs(actor.y - player_y) <= 5) then
1728               if abs(actor.x - player_x) < 0x18 and abs(actor.y - player_y) < 0x18 then
1729                  n = n + 1
1730                  avg_x = avg_x + actor.x
1731                  avg_y = avg_y + actor.y
1732               end
1733            end
1734         end
1735      end
1736   end
1737
1738   wt_num_monsters_near = n
1739
1740   if n > 0 then
1741      combat_avg_x = math.floor((avg_x + n / 2) / n)
1742      combat_avg_y = math.floor((avg_y + n / 2) / n)
1743
1744      wt_rear_max_monster = nil
1745      wt_rear_min_monster = nil
1746      --wt_rear_max_party = nil
1747      --wt_rear_min_party = nil
1748
1749      local tmp_x = combat_avg_x - party_avg_x
1750      local tmp_y = combat_avg_y - party_avg_y
1751
1752      wt_front_target_actor = nil
1753      local min_m_pos = 0
1754      local max_m_pos = 0
1755      local min_p_pos = 0
1756      local max_p_pos = 0
1757      --var_16 = 0x8000
1758
1759      local var_A
1760      for i=0,0xff do
1761         actor = Actor.get(i)
1762         if actor.obj_n ~= 0 and actor.alive and (actor.wt == WT_FRONT or actor.wt == WT_PLAYER) then
1763            --var_C = ((actor.x - party_avg_x) * tmp_x) + ((actor.y - party_avg_y) * tmp_y)
1764            var_A = ((actor.x - party_avg_x) * tmp_y) - ((actor.y - party_avg_y) * tmp_x)
1765          --  --dgb("ok "..actor.name.."\n")
1766            if actor.in_party == false then
1767               if wt_front_target_actor == nil then wt_front_target_actor = actor end
1768               if var_A > max_m_pos then max_m_pos, wt_rear_max_monster = var_A, actor end
1769               if var_A < min_m_pos then min_m_pos, wt_rear_min_monster = var_A, actor end
1770            else
1771           --dgb("yup var_A["..i.."] = "..var_A.."\n")
1772               if var_A > max_p_pos then
1773               	--dgb("var_A > max_p_pos\n")
1774               	max_p_pos, wt_rear_max_party = var_A, actor
1775               end
1776               if var_A < min_p_pos then
1777                --dgb("var_A < min_p_pos\n")
1778               	min_p_pos, wt_rear_min_party = var_A, actor
1779               end
1780               --if var_C > var_16 then var_16, unk_3DEAD = var_C, actor end
1781            end
1782         end
1783      end
1784      --FIXME I'm not really sure what the original does when these aren't set
1785      if wt_rear_max_party == nil then wt_rear_max_party = player end
1786      if wt_rear_min_party == nil then wt_rear_min_party = player end
1787   else
1788      --this is used by party members when in combat_front worktype.
1789      combat_avg_x = player_x + movement_offset_x_tbl[(player_dir*2) + 1]
1790      combat_avg_y = player_y + movement_offset_y_tbl[(player_dir*2) + 1]
1791   end
1792
1793   if combat_avg_x == party_avg_x and combat_avg_y == party_avg_y then
1794      combat_avg_x = combat_avg_x + movement_offset_x_tbl[(player_dir*2) + 1]
1795      combat_avg_y = combat_avg_y + movement_offset_y_tbl[(player_dir*2) + 1]
1796   end
1797   --dgb("PartyAvg("..party_avg_x..","..party_avg_y..") CombatAvg("..combat_avg_x..","..combat_avg_y..") Player("..player_x..","..player_y..") Dir = "..player_dir.." numMonsters = "..wt_num_monsters_near.."\n")
1798end
1799--[[
1800function trace (event, line)
1801local s = debug.getinfo(2).name
1802if s == nil then
1803	s = "Unk"
1804end
1805--io.stderr:write(s .. ":" .. line.."\n")
1806end
1807
1808   debug.sethook(trace, "l")
1809--]]
1810
1811--
1812-- actor_update_all()
1813--
1814function actor_update_all()
1815--dgb("actor_update_all()\n")
1816   if g_avatar_died == true then
1817      actor_avatar_death()
1818      return
1819   end
1820   g_time_stopped = is_time_stopped()
1821
1822   actor_calculate_avg_coords()
1823   local actor
1824   local selected_actor
1825   repeat
1826      selected_actor = nil
1827      local di = 0
1828      local dex_6 = 1
1829      repeat
1830         local player_loc = player_get_location()
1831         local var_C = (player_loc.x - 16) - (player_loc.x - 16) % 8
1832         local var_A = (player_loc.y - 16) - (player_loc.y - 16) % 8
1833
1834         for actor in party_members() do
1835            if actor.wt == WT_FOLLOW and actor.mpts < 0 then
1836               actor.mpts = 0
1837            end
1838         end
1839
1840         local player_z = player_loc.z
1841         for i=0,0xff do
1842            local actor = Actor.get(i)
1843            if actor.obj_n ~= 0 and actor.z == player_z and actor.mpts > 0 and actor.paralyzed == false and actor.asleep == false and actor.wt ~= WT_NOTHING and actor.alive == true then
1844                  if abs(actor.x - var_C) > 0x27 or abs(actor.y - var_A) > 0x27 then
1845                     if actor.wt >= 0x83 and actor.wt <= 0x86 then
1846                        --move actor to schedule location if it isn't on screen
1847                        local sched_loc = actor.sched_loc
1848                        if map_is_on_screen(sched_loc.x, sched_loc.y, sched_loc.z) == false then
1849                        	Actor.move(actor, sched_loc.x, sched_loc.y, sched_loc.z)
1850                        	actor_wt_walk_to_location(actor) --this will cancel the pathfinder and set the new worktype
1851                        	subtract_movement_pts(actor, 10)
1852                        	----dgb("\nActor SCHEDULE TELEPORT "..actor.actor_num.." to ("..sched_loc.x..","..sched_loc.y..","..sched_loc.z..")\n")
1853                        end
1854                     end
1855                  else
1856                     if actor.wt ~= WT_FOLLOW then
1857                        if actor.wt == 0x80 then
1858                           -- actor_set_worktype_from_schedule(actor)
1859                           actor.wt = actor.sched_wt
1860                        end
1861
1862                        local dx = (actor.mpts * dex_6) - actor.dex * di
1863                        if actor.mpts >= actor.dex or dx > 0 or dx == 0 and actor.dex > dex_6 then
1864                           selected_actor = actor
1865                           di = actor.mpts
1866                           dex_6 = actor.dex
1867                        end
1868
1869                        if actor.mpts >= actor.dex then
1870                           break
1871                        end
1872                     end
1873                  end
1874            end
1875         end
1876
1877         if di <= 0 then
1878            advance_time(1)
1879         end
1880      until di > 0
1881
1882      if selected_actor.corpser_flag == true then
1883         actor_corpser_regurgitation(selected_actor)
1884      end
1885
1886      if selected_actor.corpser_flag == false then
1887         if selected_actor.wt ~= WT_PLAYER and selected_actor.wt ~= WT_FOLLOW then
1888            --dgb("perform_worktype("..selected_actor.name.."("..selected_actor.actor_num..") dex = "..selected_actor.dex.." mpts = "..selected_actor.mpts..").\n")
1889            perform_worktype(selected_actor)
1890            --sub_1C34A() --update map window??
1891            if selected_actor.wt > 1 and selected_actor.wt < 0x10 then
1892               --FIXME targetting?? do *(&objlist_ptr_unk_18f1 + actor_num) = actor_num
1893            end
1894         end
1895
1896         --sub_4726()
1897      end
1898
1899   until selected_actor.obj_n ~= 0 and selected_actor.wt == WT_PLAYER and selected_actor.corpser_flag == false
1900
1901   if selected_actor ~= nil then --swap player to next party member with 'command' combat worktype.
1902    local old_player = Actor.get_player_actor()
1903	player_set_actor(selected_actor)
1904	old_player.wt = WT_PLAYER --reset worktype to player as it gets changed to follow in Player::set_actor() :-(
1905   end
1906
1907	if g_update_volcano == true then
1908		local obj = find_volcano_near_player()
1909		if obj ~= nil then
1910			if obj.obj_n == 307 then --volcano
1911				play_sfx(SFX_EARTH_QUAKE, false)
1912				quake_start(1,200)
1913			else
1914				local temp_fumarole = Obj.new(obj) --duplicate the fumarole object so we can explode it.
1915				explode_obj(temp_fumarole)
1916			end
1917		end
1918		g_update_volcano = false
1919	end
1920
1921   display_prompt(true)
1922end
1923
1924function advance_time(num_turns)
1925	local teleport = num_turns >= 60
1926	--dgb("advance_time("..num_turns..")")
1927	local time_stop_timer = timer_get(TIMER_TIME_STOP)
1928
1929	if time_stop_timer ~= 0 then
1930		if time_stop_timer > num_turns then
1931			timer_set(TIMER_TIME_STOP, time_stop_timer - num_turns)
1932			return
1933		end
1934
1935		timer_set(TIMER_TIME_STOP, 0)
1936	end
1937
1938	timer_update_all(num_turns)
1939
1940	--update keg timer. explode kegs.
1941	if g_keg_timer > 0 then
1942		g_keg_timer = g_keg_timer - num_turns
1943		if g_keg_timer <= 0 then
1944			explode_keg()
1945			g_keg_timer = 0
1946		end
1947	end
1948
1949	local random = math.random
1950	local cloak_readied = false
1951	local i
1952	for i=0,0xff do
1953		local actor = Actor.get(i)
1954
1955		if actor.obj_n ~= 0 then
1956
1957			if actor.mpts >= 0 then
1958				m = actor.dex
1959			else
1960				m = actor.mpts + actor.dex
1961			end
1962
1963			actor.mpts = m
1964			if actor.wt == WT_FOLLOW and actor.corpser_flag == true then
1965				actor_corpser_regurgitation(actor) --hack the original does this in party_move() but we don't have that in script yet.
1966			end
1967
1968			if actor.in_party == true then
1969				local obj
1970				for obj in actor_inventory(actor) do
1971					if obj.readied == true then
1972						local obj_n = obj.obj_n
1973						local obj_name = nil
1974						if obj_n == 0x5a and obj.frame_n == 1 then --lit torch
1975							if random(0, 1) == 0 then
1976								if obj.qty > num_turns then
1977									obj.qty = obj.qty - num_turns
1978								else
1979									--print("A torch burned out.\n")
1980									Actor.inv_unready_obj(actor, obj)
1981								end
1982							end
1983						elseif obj_n == 0x102 then --invisibility ring
1984							if random(0, 1000) == 429 then
1985								local two_rings = (Actor.inv_get_readied_obj_n(actor, 5) == 0x102
1986								                   and Actor.inv_get_readied_obj_n(actor, 6) == 0x102)
1987								if two_rings == false then
1988									actor.visible = true
1989								end
1990								obj_name = "ring"
1991							end
1992						elseif obj_n == 0x101 then -- regeneration ring
1993							--FIXME the original increments a counter here.
1994							local hp = actor.hp + num_turns
1995							local max_hp = actor.max_hp
1996							if hp > max_hp then
1997								hp = max_hp
1998							end
1999							actor.hp = hp
2000
2001							if random(0, 1000) == 734 then
2002								obj_name = "ring"
2003							end
2004						elseif obj_n == 0x51 then --storm cloak
2005							if random(0, 1000) == 588 then
2006								obj_name = "cloak"
2007								timer_set(TIMER_STORM, 0)
2008							else
2009								cloak_readied = true
2010							end
2011						end
2012
2013						if obj_name ~= nil then
2014							Actor.inv_remove_obj(actor, obj)
2015							print("A "..obj_name.." has vanished!\n")
2016						end
2017					end
2018				end
2019			end
2020			local j
2021			for j=1,num_turns do
2022				actor_update_flags(actor)
2023			end
2024
2025			actor_map_dmg(actor, actor.x, actor.y, actor.z)
2026			actor.hit_flag = false
2027		end
2028	end
2029
2030	if cloak_readied == true then
2031		timer_set(TIMER_STORM, 1)
2032	end
2033
2034	local minute = clock_get_minute()
2035
2036	clock_inc(num_turns)
2037
2038	if minute + num_turns >= 60 then
2039
2040		update_actor_schedules(teleport)
2041
2042		--update magic points
2043		local party_actor
2044		for party_actor in party_members() do
2045			local max_magic = actor_get_max_magic_points(party_actor)
2046			if max_magic ~= 0 then -- Avatar, Mage, Swashbuckler, Musician
2047				local magic_pts = party_actor.magic + party_actor.level
2048				if magic_pts > max_magic then
2049					magic_pts = max_magic
2050				end
2051				party_actor.magic = magic_pts
2052			end
2053		end
2054
2055		player_dec_alcohol(1)
2056	end
2057	if g_avatar_died == true then
2058		actor_avatar_death()
2059	end
2060
2061	if random(0,7) == 0 then
2062		g_update_volcano = true
2063	end
2064end
2065
2066function actor_update_flags(actor)
2067
2068	local random = math.random
2069
2070	if actor.alive then
2071		local var_8 = 0 --FIXME get proper value for var_8.
2072		if actor.visible == false and var_8 == 0 and random(0, 0x3f) == 0
2073		   and Actor.inv_get_readied_obj_n(actor, 5) ~= 0x102 --invisibility rings
2074		   and Actor.inv_get_readied_obj_n(actor, 6) ~= 0x102 then
2075			if actor_int_adj(actor) <= random(1, 0x1e) then
2076				actor.visible = true
2077			end
2078		end
2079
2080		if actor.protected == true and random(0, 0x3f) == 0 then
2081			if actor_int_adj(actor) <= random(1, 0x1e) then
2082				actor.protected = false
2083			end
2084		end
2085
2086		if actor.cursed == true and random(0, 15) == 0 then
2087			if actor_int_adj(actor) >= random(1, 0x1e) then
2088				actor.cursed = false
2089			end
2090		end
2091
2092		if actor.charmed == true and random(0, 7) == 0 then
2093			if actor_int_adj(actor) >= random(1, 0x1e) then
2094				actor_remove_charm(actor)
2095			end
2096		end
2097
2098		if actor.paralyzed == true and random(0, 3) == 0 then
2099			if actor_str_adj(actor) >= random(1, 0x1e) then
2100				actor.paralyzed = false
2101				party_update_leader()
2102			end
2103		end
2104
2105		if actor.asleep == true and actor.wt ~= WT_SLEEP and random(0, 15) == 0 then
2106			actor.asleep = false
2107			actor.hit_flag = true
2108			actor.frame_n = actor.old_frame_n
2109			if actor.in_party == true then
2110				party_update_leader()
2111			end
2112		end
2113
2114		if actor.poisoned == true and actor.protected == false and random(0, 7) == 0 then
2115			if map_is_on_screen(actor.x, actor.y, actor.z) == true then
2116				actor_hit(actor, 1)
2117			else -- don't show the animation or play the sfx
2118				actor_hit(actor, 1, nil, true)
2119			end
2120		end
2121	end
2122
2123end
2124
2125function actor_corpser_regurgitation(actor)
2126   --dgb("actor_corpser_regurgitation("..actor.name..")\n");
2127   if actor.corpser_flag == false then return end
2128
2129   if actor.wt == WT_PLAYER then
2130      print("\n"..actor.name..":\nARGH!\n")
2131   end
2132
2133   local random = math.random
2134   local val = random(1, 0x1e)
2135
2136   if val < actor_str_adj(actor) then
2137      play_sfx(SFX_CORPSER_REGURGITATE, true)
2138      print("`"..actor.name.." regurgitated!\n")
2139      actor.corpser_flag = false
2140      if actor.in_party == true then
2141         party_update_leader()
2142      end
2143   else
2144      actor_hit(actor, random(1, 0xf))
2145      actor.mpts = 0
2146   end
2147
2148end
2149
2150function actor_remove_charm(actor)
2151
2152	if actor.charmed == true then
2153		actor.charmed = false;
2154		actor.align = actor.old_align
2155
2156		if actor.in_party == true then
2157			actor.align = ALIGNMENT_GOOD
2158		end
2159		party_update_leader()
2160		--[[ FIXME need to add functions to check for combat_mode and solo_mode
2161		if party_in_combat_mode() then
2162			actor.wt = combat_mode
2163		else
2164			if solo_mode() then
2165				actor.wt = WT_NOTHING
2166			else
2167				actor.wt = WT_FOLLOW
2168			end
2169		end
2170		--]]
2171		return true
2172	end
2173
2174	return false
2175end
2176
2177function actor_yell_for_help(attacking_actor, defending_actor, dmg)
2178
2179   if defending_actor.hp == 0 then
2180     return
2181   end
2182
2183   if defending_actor.luatype == "actor" and defending_actor.align == ALIGNMENT_NEUTRAL and defending_actor.in_party == false then
2184      local actor_base = actor_tbl[defending_actor.obj_n]
2185
2186      if attacking_actor.wt == WT_PLAYER and actor_base ~= nil then
2187         if actor_base[7] ~= 0 and dmg > 0 then  --[7] == can_talk
2188            print("`"..defending_actor.name.." yells for help!\n")
2189            activate_city_guards()
2190         end
2191      end
2192
2193      if defending_actor.wt >= 0x80 then
2194         if attacking_actor.wt == WT_PLAYER then
2195            if defending_actor.obj_n == 0x188 then -- musician
2196               defending_actor.frame_n = defending_actor.old_frame_n
2197            end
2198            defending_actor.wt = WT_ATTACK_PARTY
2199         end
2200
2201      else
2202
2203         if attacking_actor.align ~= ALIGNMENT_GOOD then
2204            defending_actor.align = ALIGNMENT_GOOD
2205         else
2206            defending_actor.align = ALIGNMENT_CHAOTIC
2207         end
2208
2209         if defending_actor.temp == true then
2210            defending_actor.wt = WT_ASSAULT
2211         end
2212      end
2213
2214   end
2215
2216end
2217
2218function can_get_obj_override(obj) --meat, ribs, dead animal. These are toptile objects and need an override check.
2219	if obj.obj_n == 209 or obj.obj_n == 210 or obj.obj_n == 211 then
2220		return true
2221	end
2222	return false
2223end
2224
2225function actor_get_obj(actor, obj)
2226
2227	if obj.getable == false then
2228		print("\n\nNot possible.")
2229		return false
2230	end
2231
2232	if Actor.can_carry_obj_weight(actor, obj) == false then
2233		print("\n\nThe total is too heavy.")
2234		return false
2235	end
2236
2237	local player_loc = player_get_location()
2238	local i
2239	local caught_stealing = false
2240	if player_loc.z == 0 and obj.ok_to_take == false then --stealing logic only applies to surface level.
2241		player_subtract_karma(1)
2242		play_sfx(SFX_FAILURE, true)
2243		for i=1,202 do --perm actors
2244			local actor = Actor.get(i)
2245
2246			if actor.alive == true and actor.z == player_loc.z and actor.in_party == false and actor.align == ALIGNMENT_NEUTRAL and actor.asleep == false then
2247				if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x6 then
2248					print("\n\n\"Stop Thief!!!\"\n")
2249					activate_city_guards()
2250					caught_stealing = true
2251					break
2252				end
2253			end
2254		end
2255
2256		if caught_stealing == false then
2257			print("\n\nStealing!!!\n")
2258		end
2259	end
2260
2261	obj.ok_to_take = true
2262
2263   Obj.moveToInv(obj, actor.actor_num)
2264   subtract_movement_pts(actor, 3)
2265
2266	return true
2267end
2268
2269function activate_city_guards()
2270--dgb("activate_city_guards()")
2271   local i
2272   local player_loc = player_get_location()
2273
2274   player_subtract_karma(5)
2275
2276   for i=1,0xff do
2277      local actor = Actor.get(i)
2278
2279      if actor.alive == true and actor.z == player_loc.z and actor.in_party == false and actor.align == ALIGNMENT_NEUTRAL then
2280         if actor.obj_n == 0x17e then -- guard
2281            if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x20 then
2282               actor.wt = WT_GUARD_ARREST_PLAYER
2283            end
2284         else
2285            if actor.wt ~= WT_STATIONARY
2286             and actor.wt ~= WT_NOTHING
2287             and actor.wt ~= WT_PLAYER
2288             and actor.paralyzed == false
2289             and actor.asleep == false
2290             and actor.corpser_flag == false then
2291               if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x6 then
2292                  if actor.wt < 0x80 then
2293                     actor.wt = WT_UNK_13
2294                  else
2295                     if actor.wt > 0x86 then
2296                        if actor.obj_n == 0x188 then
2297                           actor.frame_n = actor.old_frame_n
2298                        end
2299                        actor.wt = WT_ATTACK_PARTY
2300                     end
2301                  end
2302               end
2303            end
2304         end
2305      end
2306   end
2307end
2308
2309function actor_catch_up_to_party(actor)
2310   local target_actor = nil
2311   local actor_x = actor.x
2312   local actor_y = actor.y
2313   local var_2 = 0x32
2314   local party_actor
2315   local random = math.random
2316
2317   for party_actor in party_members() do
2318
2319      if party_actor.alive == true and actor_ok_to_attack(actor, party_actor) == true then
2320
2321         local party_actor_x = party_actor.x
2322         local party_actor_y = party_actor.y
2323         local var_4 = (party_actor_x - actor_x) * (party_actor_x - actor_x) + (party_actor_y - actor_y) * (party_actor_y - actor_y)
2324         if var_4 < var_2 or var_4 == var_2 and random(0, 1) ~= 0 then
2325            var_2 = var_4
2326            target_actor = party_actor
2327         end
2328      end
2329   end
2330
2331   if target_actor == nil then
2332      actor_move_towards_loc(actor, party_avg_x, party_avg_y)
2333      return false
2334   end
2335
2336   actor_move_towards_loc(actor, target_actor.x, target_actor.y)
2337
2338   if abs(target_actor.x - actor.x) > 1 or abs(target_actor.y - actor.y) > 1 then
2339     return false
2340   end
2341
2342   return true --reached party
2343end
2344
2345function caught_by_guard(actor)
2346
2347   local lord_british = Actor.get(6)
2348
2349   --FIXME we don't want people going to jail before they answer the copy protection questions.
2350   --if (objlist_talk_flags[5] & 0x80) == 0 then
2351   --   actor.wt = 0x81
2352   --   return
2353   --end
2354
2355   Actor.show_portrait(actor)
2356
2357   print("\n\"Thou art under arrest!\"\n\n\"Wilt thou come quietly?\"\n\n:")
2358
2359   local var_6 = input_select("yn", false)
2360
2361   actor.wt = 0x81
2362
2363   Actor.hide_portrait()
2364
2365   if var_6 == "Y" then
2366      print("es\n\nThe guard strikes thee unconscious!\n\nThou dost awaken to...\n")
2367      --sub_2ACA1()
2368
2369      fade_out()
2370
2371      if party_is_in_combat_mode() then
2372         party_set_combat_mode(false)
2373      end
2374
2375      player_move(0xe7, 0xba, 0, true)
2376
2377      local cur_hour = clock_get_hour()
2378      while cur_hour ~= 8 do
2379      	advance_time(60)
2380      	cur_hour = clock_get_hour()
2381      	--FIXME need to pause and update screen so player can see the sun-moon display move.
2382      end
2383
2384      for party_actor in party_members() do
2385         for var_4 in actor_inventory(party_actor, true) do -- recursively search containers in inventory.
2386            --if((*(var_4 + objlist_obj_flags) & 0x18) == 0 || sub_CC5E(var_4, *(di + objlist_party_roster)) == 0)
2387            --   break
2388
2389            if var_4.obj_n == 0x3f then --lockpick
2390               Actor.inv_remove_obj(party_actor, var_4)
2391            end
2392
2393            if var_4.obj_n == 0x40 and var_4.quality == 9 then --key
2394
2395               local obj = map_get_obj(0xeb, 0xb7, 0, 0xb1) --desk
2396               if obj ~= nil then
2397                  Obj.moveToCont(var_4, obj)
2398               end
2399            end
2400         end
2401      end
2402
2403      local obj = map_get_obj(0xe7, 0xb8, 0, 0x12c) --steal door
2404      if obj ~= nil then
2405         obj.frame_n = 9 --close and lock door.
2406      end
2407
2408      fade_in()
2409
2410   --[[
2411      word_31D08 = 1
2412      sub_E5F2()
2413      music_play_song()
2414      ax = sub_46DC()
2415   --]]
2416   else
2417
2418      print("o\n\n\"Then defend thyself, rogue!\"\n")
2419      activate_city_guards()
2420      actor.wt = WT_ASSAULT
2421      actor.align = ALIGNMENT_EVIL
2422
2423      local i
2424      for i=1,0xff do
2425         local a = Actor.get(i)
2426         if a.wt == WT_GUARD_ARREST_PLAYER then
2427            a.wt = WT_ASSAULT
2428            a.align = ALIGNMENT_EVIL
2429         end
2430      end
2431
2432   end
2433
2434   return
2435end
2436
2437local tangle_vine_frame_n_tbl = {
24381, 3, 1, 4,
24395, 0, 4, 0,
24401, 2, 1, 5,
24412, 0, 3, 0
2442}
2443
2444function move_tanglevine(actor, new_direction)
2445   local actor_x = actor.x
2446   local actor_y = actor.y
2447   local old_direction = actor.direction
2448
2449   if new_direction ~= direction_reverse(old_direction) then
2450
2451      if actor_move(actor, new_direction, 1) ~= 0 then
2452         local player_loc = player_get_location()
2453
2454         local tangle_obj = Obj.new(0x16e) --tanglevine
2455
2456         tangle_obj.frame_n = tangle_vine_frame_n_tbl[old_direction * 4 + new_direction + 1]
2457         tangle_obj.status = 0x20
2458         tangle_obj.quality = 0 --actor.id_n
2459
2460         Obj.moveToMap(tangle_obj, actor_x, actor_y, player_loc.z)
2461
2462         actor.frame_n = tangle_vine_frame_n_tbl[new_direction * 4 + math.random(0, 3) + 1]
2463
2464         return true
2465      end
2466   end
2467
2468   return false
2469end
2470
2471function actor_wt_front(actor)
2472	--dgb("actor_wt_front("..actor.name..")\n")
2473	if actor_wt_front_1FB6E(actor) ~= 0 then
2474		if actor.in_party == true then
2475			actor_wt_attack(actor)
2476		else
2477			if actor_find_max_xy_distance(actor, party_avg_x, party_avg_y) >= 8 then
2478				subtract_movement_pts(actor, 5)
2479			else
2480				if actor ~= wt_front_target_actor then
2481					actor_wt_attack(actor)
2482				else
2483					if math.random(0, 7) ~= 0 then
2484						actor_wt_attack(actor)
2485					else
2486						actor_move_towards_loc(actor, party_avg_x, party_avg_y)
2487					end
2488				end
2489			end
2490		end
2491
2492		return
2493	end
2494
2495	return
2496end
2497
2498function actor_wt_front_1FB6E(actor)
2499   --dgb("actor_wt_front_1FB6E()\n")
2500   if (wt_num_monsters_near == 0 or wt_front_target_actor == nil) and actor.in_party == false then
2501      subtract_movement_pts(actor, 5)
2502      return 1
2503   end
2504
2505   local player_loc = player_get_location()
2506
2507   local centre_x,centre_y,diff_x,diff_y,var_1E,var_24,var_14,var_12,var_20,var_1C,var_10,target_x,target_y,var_E
2508   local actor_x = actor.x
2509   local actor_y = actor.y
2510   if actor.in_party == false then
2511      centre_x = party_avg_x
2512      centre_y = party_avg_y
2513      diff_x = wt_front_target_actor.x - centre_x
2514      diff_y = wt_front_target_actor.y - centre_y
2515   else
2516      centre_x = player_loc.x
2517      centre_y = player_loc.y
2518      diff_x = combat_avg_x - centre_x
2519      diff_y = combat_avg_y - centre_y
2520   end
2521
2522   --dgb("actor_wt_front_1FB6E() actor = ("..actor_x..","..actor_y..") centre = ("..centre_x..","..centre_y..") player = ("..player_loc.x..","..player_loc.y..")\n")
2523   var_1E = (actor_x - centre_x) * diff_y - (actor_y - centre_y) * diff_x
2524   if var_1E <= 0 then
2525      var_24 = 0
2526   else
2527      var_24 = 1
2528   end
2529
2530   if var_1E == 0 then
2531      var_24 = math.random(0, 1)
2532   end
2533
2534   if diff_y <= 0 then
2535      var_14 = -1
2536   else
2537      var_14 = 1
2538   end
2539
2540   if diff_x <= 0 then
2541      var_12 = 1
2542   else
2543      var_12 = -1
2544   end
2545
2546   if var_24 == 0 then
2547      var_14 = -var_14
2548      var_12 = -var_12
2549   end
2550
2551   local tmp_actor
2552   if actor.in_party == true then
2553      tmp_actor = Actor.get_player_actor()
2554   else
2555      tmp_actor = wt_front_target_actor
2556   end
2557
2558   --dgb("tmp_actor = "..tmp_actor.name.." at ("..tmp_actor.x..","..tmp_actor.y..")\n")
2559   var_20 = (tmp_actor.x - centre_x) * diff_x + (tmp_actor.y - centre_y) * diff_y
2560   if actor.in_party == true then
2561      var_20 = var_20 + abs(diff_x) + abs(diff_y)
2562   end
2563
2564   var_1C = diff_x * diff_x + diff_y * diff_y
2565   if var_1C == 0 then
2566      var_1C = 1
2567   end
2568
2569   --dgb("getting target var_20 = "..var_20.." diff_x = "..diff_x.." diff_y = "..diff_y.." var_1C = "..var_1C.."\n")
2570   target_x = math.floor((var_20 * diff_x) / var_1C) + centre_x
2571   target_y = math.floor((var_20 * diff_y) / var_1C) + centre_y
2572
2573   unk_30A72 = 0
2574
2575   local chunk_x = player_loc.x - 16
2576   chunk_x = chunk_x - (chunk_x % 8)
2577
2578   local chunk_y = player_loc.y - 16
2579   chunk_y = chunk_y - (chunk_y % 8)
2580
2581   local found_actor = false
2582   repeat
2583      if target_x < chunk_x or chunk_x + 0x27 < target_x or target_y < chunk_y or chunk_y + 0x27 < target_y or (actor_x == target_x and actor_y == target_y) then
2584         unk_30A72 = 1
2585         --dgb("combat_front returned. too far away. actor=("..actor_x..","..actor_y..") target=("..target_x..","..target_y..") chunk=("..chunk_x..","..chunk_y..")\n")
2586         return 1
2587      end
2588
2589      if map_get_actor(target_x, target_y, player_loc.z) ~= nil then
2590         found_actor = true
2591      else
2592         found_actor = false
2593      end
2594
2595      if found_actor then
2596         var_10 = (target_x + var_14 - centre_x) * diff_x + (target_y - centre_y) * diff_y
2597         var_E = (target_x - centre_x) * diff_x + (target_y + var_12 - centre_y) * diff_y
2598
2599         if abs(var_10 - var_20) >= abs(var_E - var_20) then
2600            target_y = target_y + var_12
2601         else
2602            target_x = target_x + var_14
2603         end
2604      end
2605   until found_actor == false
2606
2607
2608   local mpts = actor.mpts
2609   if actor_move_towards_loc(actor, target_x, target_y) ~= 0 then
2610      if actor.in_party == false or actor.x == target_x and actor.y == target_y then
2611         return 0
2612      end
2613
2614      if wt_num_monsters_near == 0 then
2615         actor.mpts = mpts
2616      end
2617
2618      if actor_move_towards_loc(actor, target_x, target_y) ~= 0 then
2619         return 0
2620      end
2621   else
2622      actor.mpts = mpts
2623   end
2624
2625   actor_wt_attack(actor)
2626
2627   return 0
2628end
2629
2630function actor_wt_rear(actor)
2631   --dgb("actor_wt_rear()\n")
2632   local var_C = 0
2633   local player_loc = player_get_location()
2634   local var_2,var_4,avg_y,avg_x,dx,ax,avg_x_diff, avg_y_diff
2635   if actor.in_party == false then
2636
2637      if wt_num_monsters_near == 0 then subtract_movement_pts(actor, 5) return end
2638
2639      var_4 = wt_rear_min_monster
2640      var_2 = wt_rear_max_monster
2641
2642      if var_4 == nil then var_4 = actor end
2643      if var_2 == nil then var_2 = actor end
2644
2645      avg_x = combat_avg_x
2646      avg_y = combat_avg_y
2647      avg_x_diff = party_avg_x - combat_avg_x
2648      avg_y_diff = party_avg_y - combat_avg_y
2649
2650   else
2651
2652      if wt_num_monsters_near == 0 then actor_move_towards_loc(actor, player_loc.x, player_loc.y) return end
2653
2654      var_4 = wt_rear_max_party
2655      var_2 = wt_rear_min_party
2656      avg_x = party_avg_x
2657      avg_y = party_avg_y
2658      avg_x_diff = combat_avg_x - party_avg_x
2659      avg_y_diff = combat_avg_y - party_avg_y
2660   end
2661
2662   local var_10 = 0x7fff
2663   local align = actor.align
2664   local i, var_12
2665   for i=0,0xff do
2666      local a = Actor.get(i)
2667
2668      if a.alive and a.wt == WT_FRONT and a.align == align then
2669
2670         var_12 = (a.x - avg_x) * avg_x_diff + (a.y - avg_y) * avg_y_diff
2671
2672         if var_12 < var_10 then var_10 = var_12 end
2673
2674      end
2675
2676   end
2677
2678   var_12 = (actor.x - avg_x) * avg_x_diff + (actor.y - avg_y) * avg_y_diff
2679   local mpts = actor.mpts
2680   local var_C
2681   if actor.in_party == false or actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) <= 3 then
2682
2683      if var_12 < var_10 then
2684
2685         var_12 = (actor.x - avg_x) * avg_y_diff - (actor.y - avg_y) * avg_x_diff
2686
2687         dx = (var_4.x - avg_x) * avg_y_diff
2688         if dx - (var_4.y - avg_y) * avg_x_diff >= var_12 then
2689
2690            dx = (var_2.x - avg_x) * avg_y_diff
2691            ax = (var_2.y - avg_y) * avg_x_diff
2692            if dx - ax <= var_12 then
2693
2694               var_C = 1
2695
2696            else
2697
2698               ax = (actor_move_towards_loc(actor, actor.x + avg_y_diff, actor.y - avg_x_diff) and -1 or 0) + 1
2699               var_C = ax
2700            end
2701
2702         else
2703
2704            ax = (actor_move_towards_loc(actor, actor.x - avg_y_diff, actor.y + avg_x_diff) and -1 or 0) + 1
2705            var_C = ax
2706         end
2707
2708      else
2709
2710         ax = (actor_move_towards_loc(actor, actor.x - avg_x_diff, actor.y - avg_y_diff) and -1 or 0) + 1
2711         var_C = ax
2712      end
2713
2714   else
2715
2716      ax = (actor_move_towards_loc(actor, player_loc.x, player_loc.y) and -1 or 0) + 1
2717      var_C = ax
2718   end
2719
2720   if var_C ~= 0 then
2721
2722      actor.mpts = mpts
2723      actor_wt_attack(actor)
2724   end
2725
2726
2727end
2728
2729function actor_wt_flank(actor)
2730
2731   local player_loc = player_get_location()
2732   local player_x = player_loc.x
2733   local player_y = player_loc.y
2734
2735   local random = math.random
2736   local abs = abs
2737
2738   if wt_num_monsters_near == 0 or actor.in_party == false and actor_find_max_xy_distance(actor, player_x, player_y) > 7 then
2739
2740      if actor.in_party == true and actor_find_max_xy_distance(actor, player_x, player_y) > 2 then
2741         local mpts = actor.mpts
2742         actor_move_towards_loc(actor, player_x, player_y)
2743         actor.mpts = mpts
2744      end
2745
2746      subtract_movement_pts(actor, 5)
2747      return
2748   end
2749
2750   local actor_align = actor.align
2751   local actor_x = actor.x
2752   local actor_y = actor.y
2753   local tmp_x = combat_avg_x - party_avg_x
2754   local tmp_y = combat_avg_y - party_avg_y
2755   local var_20 = (actor_x - party_avg_x) * tmp_y - (actor_y - party_avg_y) * tmp_x
2756
2757   local var_1E, var_10, var_E
2758
2759   if var_20 <= 0 then
2760
2761      var_1E = 0
2762   else
2763
2764      var_1E = 1
2765   end
2766
2767   if var_20 == 0 then
2768
2769      var_1E = random(0, 1)
2770   end
2771   if var_1E == 0 then
2772
2773      var_10 = -tmp_y
2774      var_E = tmp_x
2775      var_20 = -var_20
2776   else
2777
2778      var_10 = tmp_y
2779      var_E = -tmp_x
2780   end
2781
2782   local var_1A = -0x8000
2783   local target_actor = nil
2784
2785   local i, tmp_actor
2786   for i=1,0xff do
2787      tmp_actor = Actor.get(i)
2788
2789      if tmp_actor ~= nil
2790       and tmp_actor.alive == true
2791       and tmp_actor.align ~= actor_align
2792       and actor_ok_to_attack(actor, tmp_actor) == true
2793       and (actor_align ~= ALIGNMENT_GOOD or alignment_is_evil(tmp_actor.align) == true)
2794       and (actor_align ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_CHAOTIC) then
2795
2796         local target_x = tmp_actor.x
2797         local target_y = tmp_actor.y
2798
2799         if (tmp_actor.wt ~= WT_RETREAT and tmp_actor.wt ~= WT_FLEE and tmp_actor.wt ~= WT_MOUSE or player_x - 5 <= target_x and player_x + 5 >= target_x and player_y - 5 <= target_y and player_y + 5 >= target_y)
2800          and (abs(target_x - player_x) <= 7 and abs(target_y - player_y) <= 7 or abs(target_x - actor_x) <= 5 and abs(target_y - actor_y) <= 5) then
2801
2802            local var_1C = (target_x - party_avg_x) * tmp_y - (target_y - party_avg_y) * tmp_x
2803            if var_1E == 0 then
2804
2805               var_1C = -var_1C
2806            end
2807
2808            if var_1C > var_1A then
2809
2810               var_1A = var_1C
2811               target_actor = tmp_actor
2812            end
2813         end
2814      end
2815   end
2816
2817   if target_actor == nil then
2818      actor_move_towards_player(actor)
2819      return
2820   end
2821
2822   g_obj = target_actor
2823   tmp_x = target_actor.x
2824   tmp_y = target_actor.y
2825
2826   local should_move_actor = false
2827   local weapon_obj = actor_get_weapon(actor, target_actor)
2828   local weapon_range = get_weapon_range(weapon_obj.obj_n)
2829   local attack_range = Actor.get_range(actor, tmp_x, tmp_y)
2830
2831   if attack_range < 9 and attack_range <= weapon_range then
2832
2833      if map_can_reach_point(actor_x, actor_y, tmp_x, tmp_y, actor.z) == false then
2834         if random(0, 1) == 0 then
2835            tmp_x = target_actor.y - actor_y + actor_x
2836            tmp_y = actor_y - target_actor.x - actor_x
2837         else
2838            tmp_x = actor_x - target_actor.y - actor_y
2839            tmp_y = target_actor.x - actor_x + actor_y
2840         end
2841         should_move_actor = true
2842      else
2843         actor_attack(actor, tmp_x, tmp_y, actor.z, weapon_obj, target_actor)
2844         subtract_movement_pts(actor, 10)
2845      end
2846   else
2847
2848      if var_10 <= 0 then
2849
2850         if var_10 < 0 then
2851
2852            tmp_x = tmp_x - 1
2853         end
2854      else
2855
2856         tmp_x = tmp_x + 1
2857      end
2858      if var_E <= 0 then
2859         if var_E < 0 then
2860            tmp_y = tmp_y - 1
2861         end
2862      else
2863         tmp_y = tmp_y + 1
2864      end
2865      should_move_actor = true
2866   end
2867
2868   if should_move_actor == true then
2869
2870      local mpts = actor.mpts
2871      if actor_move_towards_loc(actor, tmp_x, tmp_y) == 0 then
2872
2873         actor.mpts = mpts
2874         actor_wt_attack(actor)
2875      end
2876   end
2877
2878   return
2879end
2880
2881
2882function actor_wt_berserk(actor)
2883   local target_actor = nil
2884   local alignment = actor.align
2885   local actor_x = actor.x
2886   local actor_y = actor.y
2887   local max_stats = 0
2888
2889   local i
2890   for i=1,0xff do
2891      local tmp_actor = Actor.get(i)
2892
2893      if tmp_actor ~= nil
2894       and tmp_actor.alive == true
2895       and tmp_actor.actor_num ~= actor.actor_num
2896       and actor_find_max_xy_distance(tmp_actor, actor_x, actor_y) <= 8
2897       and actor_ok_to_attack(actor, tmp_actor) == true
2898       and (alignment ~= ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_EVIL)
2899       and (alignment ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD)
2900       and (alignment ~= ALIGNMENT_CHAOTIC or (tmp_actor.align ~= ALIGNMENT_CHAOTIC and tmp_actor.align ~= ALIGNMENT_NEUTRAL)) then
2901         local combined_stats = tmp_actor.str + tmp_actor.dex + tmp_actor.int
2902         if combined_stats > max_stats then
2903            max_stats = combined_stats
2904            target_actor = tmp_actor
2905         end
2906      end
2907   end
2908
2909   if target_actor == nil then
2910      actor_wt_attack(actor)
2911      subtract_movement_pts(actor, 5)
2912      return
2913   end
2914
2915   local target_x = target_actor.x
2916   local target_y = target_actor.y
2917   local weapon_obj = actor_get_weapon(actor, target_actor)
2918   local weapon_range = get_weapon_range(weapon_obj.obj_n)
2919   local attack_range = Actor.get_range(actor, target_x, target_y)
2920
2921   if attack_range < 9 and attack_range <= weapon_range then
2922
2923      --g_obj = target_actor
2924      if map_can_reach_point(actor_x, actor_y, target_x, target_y, actor.z) == false then
2925
2926         if math.random(0, 1) == 0 then
2927            target_x = target_actor.y - actor_y + actor_x
2928            target_y = actor_y - target_actor.x - actor_x
2929         else
2930            target_x = actor_x - target_actor.y - actor_y
2931            target_y = target_actor.x - actor_x + actor_y
2932         end
2933      else
2934         actor_attack(actor, target_x, target_y, actor.z, weapon_obj, target_actor)
2935         subtract_movement_pts(actor, 15)
2936         return
2937      end
2938
2939   end
2940
2941   local mpts = actor.mpts
2942   if actor_move_towards_loc(actor, target_x, target_y) == 0 then
2943      actor.mpts= mpts
2944      actor_wt_attack(actor)
2945   end
2946
2947   subtract_movement_pts(actor, 5)
2948   return
2949end
2950
2951function actor_wt_guard_arrest_player(actor)
2952	if actor_catch_up_to_party(actor) == true then
2953		caught_by_guard(actor)
2954	end
2955end
2956
2957function actor_wt_combat_tanglevine(actor)
2958   local random = math.random
2959   local di
2960   local abs = abs
2961
2962   local target = actor_find_target(actor)
2963   if target ~= nil then
2964
2965      local target_x = target.x
2966      local target_y = target.y
2967      local actor_x = actor.x
2968      local actor_y = actor.y
2969      if abs(target_x - actor_x) < 2 and abs(target_y - actor_y) < 2 and random(0, 1) ~= 0 then
2970
2971         actor_attack(actor, target_x, target_y, actor.z, actor, target)
2972         subtract_movement_pts(actor, 10)
2973         return
2974      end
2975
2976      if abs(target_x - actor_x) < 5 and abs(target_y - actor_y) < 5 and random(0, 3) == 0 then
2977
2978         target_x = target_x - actor_x
2979         target_y = target_y - actor_y
2980
2981         if abs(target_x) <= abs(target_y) then
2982
2983            di = (target_y <= 0) and DIR_NORTH or DIR_SOUTH
2984
2985            if move_tanglevine(actor, di) == 0  then
2986
2987               di = (target_x <= 0) and DIR_WEST or DIR_EAST
2988               move_tanglevine(actor, di)
2989               return
2990            end
2991
2992         else
2993
2994            di = (target_x <= 0) and DIR_WEST or DIR_EAST
2995
2996            if move_tanglevine(actor, di) == 0 then
2997
2998               di = (target_y <= 0) and DIR_NORTH or DIR_SOUTH
2999               move_tanglevine(actor, di)
3000               return
3001            end
3002         end
3003      end
3004   end
3005
3006   if random(0, 3) == 0 then
3007
3008      di = random(0, 3) --random direction north south east west
3009      if actor.direction == di then
3010
3011         di = direction_reverse(di)
3012      end
3013
3014      move_tanglevine(actor, di)
3015
3016   else
3017      actor.mpts = 0
3018   end
3019
3020   return
3021end
3022
3023
3024function actor_wt_combat_stationary(actor)
3025   local rand = math.random
3026   local align = actor.align
3027   if align == ALIGNMENT_NEUTRAL then
3028
3029      subtract_movement_pts(actor, 5)
3030      return
3031   end
3032
3033   local i
3034   for i=1,0x10 do
3035
3036      local target_x = rand(0, 8) + actor.x - 4
3037      local target_y = rand(0, 8) + actor.y - 4
3038--      local g_obj = sub_1D351(actor, target_x, target_y)
3039      local target_actor = map_get_actor(target_x, target_y, actor.z)
3040
3041      if target_actor ~= nil and actor_ok_to_attack(actor, target_actor) == true and target_actor.alive == true and target_actor.align ~= align and target_actor.align ~= ALIGNMENT_NEUTRAL then
3042
3043         actor_attack(actor, target_x, target_y, actor.z, actor_get_weapon(actor, target_actor), target_actor)
3044         subtract_movement_pts(actor, 10)
3045         return
3046      end
3047
3048   end
3049
3050   subtract_movement_pts(actor, 5)
3051
3052   return
3053end
3054
3055function actor_wt_walk_straight(actor)
3056   if math.random(0, 1) == 0 then subtract_movement_pts(actor, 5) return end
3057
3058   local wt = actor.wt
3059   local sched = actor.sched_loc
3060   local dir
3061
3062   if wt < WT_WALK_NORTH_SOUTH or actor.x ~= sched.x or actor.y ~= sched.y then
3063      dir = actor.direction
3064   else
3065      if wt == WT_WALK_NORTH_SOUTH then dir = DIR_NORTH end
3066      if wt == WT_WALK_EAST_WEST then dir = DIR_EAST end
3067      if wt == WT_WALK_SOUTH_NORTH then dir = DIR_SOUTH end
3068      if wt == WT_WALK_WEST_EAST then dir = DIR_WEST end
3069      actor.direction = dir
3070   end
3071
3072   local mpts = actor.mpts
3073
3074   if actor_move(actor, dir, 1) == 0 then
3075
3076      dir = direction_reverse(dir)
3077
3078      actor.mpts = mpts
3079      actor_move(actor, dir, 1)
3080      actor.direction = dir
3081   end
3082
3083end
3084
3085function actor_wt_wander_around(actor)
3086   local rand = math.random
3087   if rand(0, 7) ~= 0 then subtract_movement_pts(actor, 5) return end
3088
3089   local random_wander_range = function ()
3090      local i = 0
3091      while rand(0, 1) ~= 0 do i = i + 1 end
3092      if rand(0, 1) ~= 0 then i = -i end
3093      return i
3094   end
3095
3096   local abs=abs
3097   local sched = actor.sched_loc
3098   local sched_x_offset = actor.x - sched.x;
3099   local sched_y_offset = actor.y - sched.y;
3100   local direction
3101
3102   if abs(sched_y_offset) - abs(sched_x_offset) >= random_wander_range() then
3103      direction = (random_wander_range() <= sched_y_offset) and DIR_NORTH or DIR_SOUTH
3104   else
3105      direction = (random_wander_range() <= sched_x_offset) and DIR_WEST or DIR_EAST
3106   end
3107
3108   actor.direction = direction
3109   actor_move(actor, direction, 1)
3110
3111   return
3112end
3113
3114function actor_move_towards_player(actor)
3115local rand = math.random
3116if actor.wt ~= WT_STATIONARY then
3117   if actor.in_party == true then
3118      local player_loc = player_get_location()
3119      local x,y = player_loc.x, player_loc.y
3120      if actor.x ~= x and actor.y ~= y and rand(0, 3) == 0 then
3121         actor_move_towards_loc(actor, x, y)
3122         return
3123      end
3124   end
3125
3126   if rand(0, 7) == 0 then
3127      actor_move(actor, rand(0, 3), 1)
3128      return
3129   end
3130end
3131subtract_movement_pts(actor, 5)
3132end
3133
3134function actor_wt_attack(actor)
3135--dgb("actor_wt_attack()\n");
3136
3137   g_obj = actor_find_target(actor)
3138   if g_obj ~= nil then
3139   	--dgb("target at ("..g_obj.x..","..g_obj.y..")\n")
3140   end
3141
3142   local weapon_obj = actor_get_weapon(actor, g_obj)
3143
3144   if g_obj ~= nil then
3145
3146      local target_x = g_obj.x
3147      local target_y = g_obj.y
3148      local actor_x = actor.x
3149      local actor_y = actor.y
3150      local weapon_range = get_weapon_range(weapon_obj.obj_n)
3151
3152      if abs(target_x - actor_x) < 8 and abs(target_y - actor_y) < 8 and
3153       Actor.get_range(actor, target_x, target_y) <= weapon_range then
3154
3155         if sub_1D59F(actor, target_x, target_y, weapon_range, 0) == true then
3156            actor_attack(actor, g_obj.x, g_obj.y, g_obj.z, weapon_obj, g_obj)
3157            subtract_movement_pts(actor, 10)
3158            return
3159         end
3160
3161         if math.random(0, 1) == 0 then
3162
3163            target_x = g_obj.y - actor_y + actor_x
3164            target_y = actor_y - g_obj.x - actor_x
3165
3166         else
3167
3168            target_x = actor_x - g_obj.y - actor_y
3169            target_y = g_obj.x - actor_x + actor_y
3170         end
3171
3172         actor_move_towards_loc(actor, target_x, target_y)
3173         return
3174      end
3175
3176      if actor.wt ~= 3 then actor_move_towards_loc(actor, target_x, target_y) return end
3177
3178      subtract_movement_pts(actor, 5)
3179      return
3180   end
3181
3182   if actor.wt ~= 3 then actor_move_towards_player(actor) return end
3183
3184
3185   subtract_movement_pts(actor, 5)
3186
3187end
3188
3189function actor_wt_timid(actor)
3190	local player_loc = player_get_location()
3191	if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) > 7 then
3192	   actor.mpts = 0
3193	   return
3194	end
3195	local target = actor_find_target(actor)
3196	if target == nil then
3197	   actor_move_towards_player(actor)
3198	   return
3199	end
3200	local diff_x = target.x - actor.x
3201	local diff_y = target.y - actor.y
3202	local var_4
3203	if actor.wt ~= WT_RETREAT then
3204	   var_4 = 3
3205	else
3206	   var_4 = 8
3207	end
3208
3209	if abs(diff_x) < var_4 and abs(diff_y) < var_4 then
3210	   local var_2 = actor.mpts
3211	   if actor_move_towards_loc(actor, actor.x - diff_x, actor.y - diff_y) == 0 then
3212	      --original logic -- actor.mpts = var_2
3213	      if actor.wt ~= WT_MOUSE and actor.wt ~= WT_FLEE then
3214	         actor.mpts = var_2 --moved here.
3215	         actor_wt_attack(actor)
3216	      end
3217	   end
3218	else
3219	   if actor.align == ALIGNMENT_GOOD and actor.wt ~= WT_FLEE and actor.wt ~= WT_MOUSE then
3220	      actor_move_towards_loc(actor, party_avg_x, party_avg_y)
3221	   else
3222	      actor_move_towards_player(actor)
3223	   end
3224	end
3225
3226	if actor.wt == WT_RETREAT then
3227	   if math.random(0, 3) == 0 then
3228	      if actor.level > actor.hp then
3229	         actor.hp = actor.hp + 1
3230	      end
3231
3232	      if math.floor((actor.hp * 4) / actor.level) > 0 then
3233	         actor.wt = actor.combat_mode
3234	      end
3235	   end
3236	end
3237end
3238
3239function actor_wt_like(actor)
3240	local actor_x = actor.x
3241	local actor_y = actor.y
3242	local party_actor
3243	local random = math.random
3244
3245	for party_actor in party_members() do
3246
3247	   if abs(party_actor.x - actor_x) < 3 and abs(party_actor.y - actor_y) < 3 then
3248	      if random(0, 1) == 0 then
3249	         actor_move_towards_loc(actor, party_avg_x, party_avg_y)
3250	         return
3251	      end
3252
3253	      break
3254	   end
3255	end
3256
3257	if random(0, 1) == 0 then
3258	   subtract_movement_pts(actor, 5)
3259	else
3260	   if random(0, 1) == 0 then
3261	      if random(0, 1) == 0 then
3262	         actor_y = actor_y - 10
3263	      else
3264	         actor_y = actor_y + 10
3265	      end
3266	   else
3267	      if random(0, 1) == 0 then
3268	         actor_x = actor_x - 10
3269	      else
3270	         actor_x = actor_x + 10
3271	      end
3272	   end
3273	   actor_move_towards_loc(actor, actor_x, actor_y)
3274	end
3275
3276	return
3277end
3278
3279function actor_wt_unfriendly(actor)
3280	local actor_x = actor.x
3281	local actor_y = actor.y
3282	local party_actor
3283	local random = math.random
3284
3285	for party_actor in party_members() do
3286	   if abs(party_actor.x - actor_x) < 3 and abs(party_actor.y - actor_y) < 3 then
3287
3288	      if random(0, 7) == 0 then
3289	         actor.wt = WT_ASSAULT
3290	         actor_wt_attack(actor)
3291	         return
3292	      end
3293
3294	      break
3295	   end
3296	end
3297
3298	if random(0, 1) == 0 then
3299	   subtract_movement_pts(actor, 5)
3300	else
3301	   if random(0, 1) == 0 then
3302	      if random(0, 1) == 0 then
3303	         actor_y = actor_y - 10
3304	      else
3305	         actor_y = actor_y + 10
3306	      end
3307	   else
3308	      if random(0, 1) == 0 then
3309	         actor_x = actor_x - 10
3310	      else
3311	         actor_x = actor_x + 10
3312	      end
3313	   end
3314	   actor_move_towards_loc(actor, actor_x, actor_y)
3315	end
3316
3317	return
3318end
3319
3320function actor_wt_walk_to_location(actor)
3321	Actor.walk_path(actor)
3322	if Actor.is_at_scheduled_location(actor) then
3323		actor.wt = actor.sched_wt
3324		if actor.wt == WT_SLEEP then
3325			-- add extinguish light source code if actors with a light source actually go to sleep
3326			actor_wt_sleep(actor)
3327		end
3328	end
3329
3330	subtract_movement_pts(actor, 5) --FIXME what's the movement cost for pathfinding?
3331end
3332
3333function actor_wt_sleep(actor)
3334--dgb("actor_wt_sleep("..actor.name..") at "..actor.x..","..actor.y..","..actor.z.."\n")
3335	local obj = map_get_obj(actor.x, actor.y, actor.z, 0xa3) --bed
3336	if obj ~= nil then
3337		if obj.frame_n == 1 or obj.frame_n == 5 then --horizontal bed
3338			actor.obj_n = 0x92 -- person sleeping
3339			actor.frame_n = 0
3340		else
3341			if obj.frame_n == 7 or obj.frame_n == 10 then --vertical bed
3342				actor.obj_n = 0x92 -- person sleeping
3343				actor.frame_n = 1
3344			end
3345		end
3346	end
3347end
3348
3349function actor_wt_beg(actor)
3350	local player_loc = player_get_location()
3351	if actor.z == player_loc.z and abs(actor.x - player_loc.x) < 5 or abs(actor.y - player_loc.y) < 5 then
3352		if actor_catch_up_to_party(actor) == true then
3353			actor.wt = WT_WORK
3354			Actor.talk(actor)
3355		end
3356	else
3357		actor_wt_wander_around(actor)
3358	end
3359end
3360
3361function actor_wt_brawling(actor)
3362	if math.random(0, 3) == 0 then
3363		local target = actor_find_target(actor)
3364		local player_loc = player_get_location()
3365		if target ~= nil and actor_find_max_xy_distance(target, player_loc.x, player_loc.y) < 5 then
3366			if actor_find_max_xy_distance(actor, target.x, target.y) < 2 then
3367				hit_anim(target.x, target.y)
3368				actor_move_towards_loc(actor, player_loc.x, player_loc.y)
3369			else
3370				actor_move_towards_loc(actor, target.x, target.y)
3371			end
3372
3373			return
3374		end
3375	end
3376
3377	actor_wt_wander_around(actor)
3378end
3379
3380wt_tbl = {
3381[WT_NOTHING] = {"WT_NOTHING", perform_worktype},
3382[WT_FRONT] = {"WT_FRONT", actor_wt_front},
3383[WT_REAR] = {"WT_REAR", actor_wt_rear},
3384[WT_FLANK] = {"WT_FLANK", actor_wt_flank},
3385[WT_BERSERK] = {"WT_BERSERK", actor_wt_berserk},
3386[WT_RETREAT] = {"WT_RETREAT", actor_wt_timid},
3387[WT_ASSAULT] = {"WT_ASSAULT", actor_wt_attack},
3388[WT_FLEE] = {"WT_FLEE", actor_wt_timid},
3389[WT_LIKE] = {"WT_LIKE", actor_wt_like},
3390[WT_UNFRIENDLY] = {"WT_UNFRIENDLY", actor_wt_unfriendly},
3391[WT_WANDER_NEAR_PLAYER] = {"WT_WANDER_NEAR_PLAYER", actor_move_towards_player},
3392[WT_TANGLE] = {"WT_TANGLE", actor_wt_combat_tanglevine},
3393[WT_STATIONARY] = {"WT_STATIONARY", actor_wt_combat_stationary},
3394[WT_GUARD_WALK_EAST_WEST] = {"WT_GUARD_WALK_EAST_WEST", actor_wt_walk_straight},
3395[WT_GUARD_WALK_NORTH_SOUTH] = {"WT_GUARD_WALK_NORTH_SOUTH", actor_wt_walk_straight},
3396--11
3397[WT_GUARD_ARREST_PLAYER] = {"WT_GUARD_ARREST_PLAYER", actor_wt_guard_arrest_player},
3398[WT_UNK_13] = {"WT_UNK_13", actor_wt_timid},
3399[WT_WALK_TO_LOCATION] = {"WT_WALK_TO_LOCATION", actor_wt_walk_to_location},
3400[WT_WALK_NORTH_SOUTH] = {"WT_WALK_NORTH_SOUTH", actor_wt_walk_straight},
3401[WT_WALK_EAST_WEST]   = {"WT_WALK_EAST_WEST", actor_wt_walk_straight},
3402[WT_WALK_SOUTH_NORTH] = {"WT_WALK_SOUTH_NORTH", actor_wt_walk_straight},
3403[WT_WALK_WEST_EAST]   = {"WT_WALK_WEST_EAST", actor_wt_walk_straight},
3404[WT_WANDER_AROUND] = {"WT_WANDER_AROUND", actor_move_towards_player},
3405[WT_WORK] = {"WT_WORK", actor_wt_wander_around},
3406[WT_UNK_94] = {"WT_UNK_94", actor_wt_wander_around},
3407[WT_BEG] = {"WT_BEG", actor_wt_beg},
3408[WT_ATTACK_PARTY] = {"WT_ATTACK_PARTY", actor_wt_attack},
3409[WT_BRAWLING] = {"WT_BRAWLING", actor_wt_brawling},
3410[WT_MOUSE] = {"WT_MOUSE", actor_wt_timid}
3411
3412--[WT_] = {"WT_", actor_wt_rear}
3413}
3414
3415function actor_ok_to_attack(actor, target_actor)
3416
3417   if target_actor.visible == false and (target_actor.in_party == false or actor.in_party == false) then return false end
3418
3419   if target_actor.z ~= actor.z then return false end
3420
3421   if target_actor.obj_n == 0x165 and target_actor.frame_n == 0 then return false end --corpser underground
3422
3423   if target_actor.corpser_flag == true then return false end
3424
3425   --FIXME need to check tileflag3 bit 4 is not set. The Ignore flag.
3426   return true
3427
3428end
3429
3430function actor_find_target(actor)
3431
3432   local target_actor = nil
3433   local align = actor.align
3434   local actor_x = actor.x
3435   local actor_y = actor.y
3436   local var_2 = 0x7fff
3437   local i
3438   local player_loc = player_get_location()
3439   local player_x = player_loc.x
3440   local player_y = player_loc.y
3441
3442   for i=0,0xff do
3443
3444      local tmp_actor = Actor.get(i)
3445
3446      if tmp_actor.obj_n ~= 0 and tmp_actor.alive == true and tmp_actor.actor_num ~= actor.actor_num and actor_ok_to_attack(actor, tmp_actor) == true then
3447
3448         if actor.wt == WT_FLEE or
3449            actor.wt == WT_MOUSE or
3450            actor.wt == WT_UNK_13 or
3451            actor.wt == WT_RETREAT or
3452            actor.wt == WT_BRAWLING or
3453            (align ~= ALIGNMENT_NEUTRAL or actor.wt == WT_ATTACK_PARTY and tmp_actor.align == ALIGNMENT_GOOD) and
3454            (align ~= ALIGNMENT_CHAOTIC or tmp_actor.align ~= ALIGNMENT_CHAOTIC) and
3455            tmp_actor.align ~= ALIGNMENT_NEUTRAL and
3456            (align ~= ALIGNMENT_GOOD or alignment_is_evil(tmp_actor.align) == true) and
3457            (align ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_CHAOTIC) then
3458
3459            local target_x = tmp_actor.x
3460            local target_y = tmp_actor.y
3461
3462            if actor_find_max_xy_distance(actor, target_x, target_y) <= 8 and (tmp_actor.wt ~= WT_RETREAT or abs(target_x - player_x) <= 5 and abs(target_y - player_y) <= 5) then
3463
3464               local var_6 = (target_x - actor_x)^2 + (target_y - actor_y)^2
3465               if var_6 < var_2 or var_6 == var_2 and math.random(0, 1) ~= 0 then
3466
3467                  var_2 = var_6
3468                  target_actor = tmp_actor
3469               end
3470            end
3471         end
3472      end
3473
3474   end
3475
3476   return target_actor
3477end
3478
3479--FIXME this is a line of sight check for combat.
3480function sub_1D59F(actor, target_x, target_y, weapon_obj)
3481   return map_can_reach_point(actor.x, actor.y, target_x, target_y, actor.z)
3482end
3483
3484
3485
3486function perform_worktype(actor)
3487
3488   if g_time_stopped == true then
3489      actor.mpts = 0
3490      return
3491   end
3492
3493   if wt_tbl[actor.wt] == nil then
3494      subtract_movement_pts(actor, 5)
3495      return
3496   end
3497
3498   --dgb("wt = "..wt_tbl[actor.wt][1].."\n")
3499
3500   if actor.mpts > 0 then
3501   	local func = wt_tbl[actor.wt][2]
3502   	func(actor)
3503   end
3504
3505   if actor.mpts == 0 then subtract_movement_pts(actor, 0xa) end
3506
3507end
3508
3509function spell_put_actor_to_sleep(attacker, foe)
3510
3511   --dgb("spell_put_actor_to_sleep("..attacker.name..",foe)\n")
3512
3513   local actor_base = actor_tbl[foe.obj_n]
3514   if actor_base == nil or actor_base[21] == 0 then -- 21 is immune to sleep
3515      if actor_int_check(foe, attacker) == false then
3516         hit_anim(foe.x, foe.y)
3517         actor_put_to_sleep(foe)
3518         return 0xfe
3519      else
3520         return 1
3521      end
3522   end
3523
3524   return 2
3525end
3526
3527function spell_poison_actor(attacker, foe)
3528	local actor_base = actor_tbl[foe.obj_n]
3529	if actor_base == nil or actor_base[19] == 1 or foe.actor_num == 0 then return 2 end --immune to poison
3530
3531	if math.floor((math.floor(actor_str_adj(foe) / 2) + 0x1e - actor_int_adj(attacker)) / 2) <= math.random(1, 0x1e) then
3532		foe.poisoned = true
3533		hit_anim(foe.x, foe.y)
3534		return -1
3535	end
3536
3537	return 1
3538end
3539
3540function spell_take_fire_dmg(attacker, foe)
3541   local actor_base = actor_tbl[foe.obj_n]
3542   if actor_base == nil or actor_base[18] == 1 then return end --immune to magic
3543
3544   local dmg = math.random(1, 0x14)
3545
3546   if actor_base == nil or actor_base[17] == 1 then dmg = dmg * 2 end --double dmg from fire
3547
3548   local exp = actor_hit(foe, dmg)
3549   if exp ~= 0 then
3550      attacker.exp = attacker.exp + exp
3551   end
3552
3553   actor_yell_for_help(attacker, foe, 1)
3554   actor_hit_msg(foe)
3555end
3556
3557function spell_charm_actor(attacker, foe)
3558	if actor_int_check(foe, attacker) == true then return false end
3559
3560	if foe.charmed == true then
3561		actor_remove_charm(foe)
3562	else
3563		foe.charmed = true
3564		foe.old_align = foe.align
3565		foe.align = attacker.align
3566		hit_anim(foe.x, foe.y)
3567		print("\n"..foe.name.." is charmed.\n")
3568		if foe.in_party == true then
3569			party_update_leader()
3570		end
3571	end
3572
3573	return true
3574end
3575
3576function spell_kill_actor(attacker, foe)
3577	local actor_base = actor_tbl[foe.obj_n]
3578	if actor_base ~= nil and actor_base[20] == 1 then return 2 end --immune to corp spells
3579
3580	if actor_int_check(foe, attacker) == true then return 1 end
3581
3582	local exp = actor_hit(foe, foe.hp)
3583	if exp ~= 0 then
3584		attacker.exp = attacker.exp + exp
3585	end
3586
3587	actor_yell_for_help(attacker, foe, 1)
3588	actor_hit_msg(foe)
3589
3590	return -1
3591end
3592
3593function spell_hit_actor(attacker, foe, spell_num)
3594
3595	if actor_int_check(foe, attacker) == true then return false end
3596	local random = math.random
3597	local dmg = 0
3598
3599	if spell_num == 5 then
3600		dmg = random(1, 0xa)
3601	elseif spell_num == 0x62 then
3602		dmg = random(1, 0x1e)
3603	elseif spell_num == 0x32 then
3604		dmg = foe.hp - 1
3605	end
3606
3607	print("\n")
3608
3609	local exp = actor_hit(foe, dmg)
3610	if exp ~= 0 then
3611		attacker.exp = attacker.exp + exp
3612	end
3613
3614	actor_hit_msg(foe)
3615
3616	actor_yell_for_help(attacker, foe, 1)
3617
3618	return true
3619end
3620
3621function actor_use_effect(actor, effect)
3622	local random = math.random
3623	local effect_type = random(0, 3)
3624    Obj.removeFromEngine(effect) -- moved here so it won't be seen in chest
3625	if effect_type == 0 then
3626		print("Acid!\n")
3627		actor_hit(actor, random(1, 0x14))
3628
3629	elseif effect_type == 1 then
3630		print("Poison!\n")
3631		actor.poisoned = true
3632		hit_anim(actor.x, actor.y)
3633
3634	elseif effect_type == 2 then
3635		print("Bomb!\n")
3636		local hit_items = explosion(0x17e, actor.x, actor.y)
3637
3638		for k,v in pairs(hit_items) do
3639			if v.luatype == "actor" then
3640				actor_hit(v, random(1, 0x14))
3641			end
3642		end
3643
3644	elseif effect_type == 3 then
3645		print("Gas!\n")
3646		local hit_items = explosion(0x17c, actor.x, actor.y)
3647
3648		for k,v in pairs(hit_items) do
3649			if v.luatype == "actor" then
3650				v.poisoned = true
3651				hit_anim(v.x, v.y)
3652			end
3653		end
3654	end
3655
3656end
3657
3658function actor_resurrect(actor)
3659	local karma = player_get_karma()
3660	if karma < 98 then
3661		actor.exp = math.floor((actor.exp * karma) / 100)
3662	end
3663end
3664
3665function get_LB_to_throne()
3666	local cur_hour = clock_get_hour()
3667-- LB is on throne at 8-12 and 14-18
3668	while cur_hour < 8 or cur_hour > 17 or (cur_hour > 11 and cur_hour < 14) do
3669		advance_time(60)
3670		cur_hour = clock_get_hour()
3671	end
3672end
3673
3674function actor_avatar_death()
3675
3676	--FIXME the hit tile is displayed constantly while the death tune is playing.
3677	g_avatar_died = false -- before get_LB_to_throne()
3678	local avatar = Actor.get(1)
3679	if avatar.obj_n == 431 then --horse with rider
3680		Actor.use(avatar) --dismount from horse
3681	end
3682	avatar.asleep = true --we do this so it looks like the avatar is dead.
3683	print("\nAn unending darkness engulfs thee...\n\n")
3684	play_sfx(SFX_AVATAR_DEATH, true)
3685	fade_out()
3686	print("A voice in the darkness intones, \"KAL LOR!\"\n")
3687	play_sfx(SFX_KAL_LOR, true)
3688	avatar.asleep = false
3689	party_dismount_from_horses();
3690	if g_armageddon == false then
3691		party_resurrect_dead_members()
3692	end
3693
3694	--unready inventory objects.
3695	for obj in actor_inventory(avatar) do
3696		if obj.readied and obj.obj_n ~= 76 then -- don't display message for amulet of submission
3697				Actor.inv_unready_obj(avatar, obj)
3698		end
3699	end
3700	party_heal() -- need to heal before player_move
3701	party_update_leader()
3702	player_move(0x133, 0x160, 0, true)
3703	party_exit_vehicle(0x133, 0x160, 0)
3704	get_LB_to_throne()
3705
3706	for i=0,0xff do
3707		local actor = Actor.get(i)
3708		actor.mpts = 0
3709		if actor.temp == false and actor.wt == WT_ATTACK_PARTY then
3710			actor.wt = WT_WALK_TO_LOCATION
3711		end
3712	end
3713	avatar.mpts=1
3714
3715	actor_resurrect(avatar)
3716	party_set_combat_mode(false)
3717	party_set_party_mode()
3718	fade_in()
3719end
3720
3721--io.stderr:write("actor.lua loaded\n")
3722