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