Files
scummvm/devtools/create_ultima/files/ultima6/scripts/u6/actor.lua
T

3722 lines
106 KiB
Lua

--io.stderr:write("actor.lua get here\n")
--Worktypes
WT_NOTHING = 0x0 --do nothing
WT_FOLLOW = 0x1 --follow avatar (in party)
WT_PLAYER = 0x2 --player mode
WT_FRONT = 0x3 --combat front
WT_REAR = 0x4 --combat rear
WT_FLANK = 0x5 --combat flank
WT_BERSERK = 0x6 --combat berserk
WT_RETREAT = 0x7 --combat retreat
WT_ASSAULT = 0x8 --combat assault
WT_FLEE = 0x9 --run away from party
WT_LIKE = 0xa --like the party. eg dog
WT_UNFRIENDLY = 0xb --wander around but attack if any party member gets too close.
WT_WANDER_NEAR_PLAYER = 0xc --guards use this.
WT_TANGLE = 0xd --tangle vine
WT_STATIONARY = 0xe --stationary attack
WT_GUARD_WALK_EAST_WEST = 0xf
WT_GUARD_WALK_NORTH_SOUTH = 0x10
--11
WT_GUARD_ARREST_PLAYER = 0x12
WT_UNK_13 = 0x13 --FIXME I think this is repel undead
WT_WALK_TO_LOCATION = 0x86
WT_FACE_NORTH = 0x87
WT_FACE_EAST = 0x88
WT_FACE_SOUTH = 0x89
WT_FACE_WEST = 0x8a
WT_WALK_NORTH_SOUTH = 0x8b
WT_WALK_EAST_WEST = 0x8c
WT_WALK_SOUTH_NORTH = 0x8d
WT_WALK_WEST_EAST = 0x8e
WT_WANDER_AROUND = 0x8f
WT_WORK = 0x90
WT_SLEEP = 0x91
WT_UNK_94 = 0x94
WT_PLAY_LUTE = 0x95
WT_BEG = 0x96
WT_BELL_RINGER = 0x98
WT_BRAWLING = 0x99
WT_MOUSE = 0x9a
WT_ATTACK_PARTY = 0x9b
wt_rear_max_monster = nil
wt_rear_min_monster = nil
wt_rear_max_party = nil
wt_rear_min_party = nil
combat_avg_x, combat_avg_y, party_avg_x, party_avg_y = -1, -1, -1, -1
wt_front_target_actor = nil
wt_num_monsters_near = 0
g_time_stopped = false
g_update_volcano = false;
--Actor stats table
--[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()}
actor_tbl = {
--Acid Slug
[364] = {5, 5, 2, 10, 1, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
--Alligator
[429] = {20, 10, 3, 30, 10, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, {}, {}, {}, {}, 6},
--Ant, Giant
[427] = {16, 15, 2, 10, 8, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, {}, {}, {}, {}, 3},
--Avatar
[410] = {25, 25, 25, 30, 4, ALIGNMENT_GOOD, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Bat, Giant
[344] = {5, 25, 4, 5, 6, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Peasant
[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},
--Bird
[356] = {1, 28, 4, 3, 1, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
----Child
[381] = {8, 8, 9, 6, 2, ALIGNMENT_CHAOTIC, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Corpser
[357] = {17, 6, 3, 40, 15, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
--Cow
[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},
--Cyclops
[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},
--Daemon
[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},
--Deer
[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},
--Dog
[349] = {5, 16, 8, 6, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Dragon
[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},
--Drake
[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},
--Woman
[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},
--Farmer
[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},
--Fighter
[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},
--Gargoyle, Winged
[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},
--Gargoyle, Worker
[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},
--Gazer
[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},
--Ghost
[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},
--Gremlin
[353] = {2, 26, 7, 5, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Guard
[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},
--Headless
[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},
--Horse
[430] = {22, 27, 7, 15, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Horse with rider
[431] = {22, 27, 7, 15, 3, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Hydrae
[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},
--Insects
[343] = {1, 40, 1, 1, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, {}, {}, {}, {}, 0},
--Jester
[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},
--Lord British
[409] = {30, 30, 30, 255, 255, ALIGNMENT_GOOD, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, {}, {}, {}, {}, 30},
--Musician
[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},
--Mage
[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},
--Merchant
[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},
--Mimic
[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},
--Mongbat
[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},
--Mouse
[354] = {2, 25, 3, 5, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Rabbit
[359] = {3, 9, 3, 5, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Rat, Giant
[342] = {5, 13, 4, 8, 6, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, {}, {}, {}, {}, 0},
--Reaper
[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},
--Rot Worm
[360] = {3, 17, 2, 5, 2, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, {}, {}, {}, {}, 0},
--Scorpion, Giant
[426] = {13, 13, 3, 20, 8, ALIGNMENT_CHAOTIC, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, {}, {}, {}, {}, 3},
--Sea Serpent
[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},
--Sheep
[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},
--Ship
[412] = {30, 30, 30, 30, 30, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 30},
--Silver Serpent
[413] = {60, 21, 8, 200, 60, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 15},
--Skeleton
[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},
--Slime
[375] = {6, 6, 2, 15, 4, ALIGNMENT_CHAOTIC, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, {}, {}, {}, {}, 0},
--Snake
[358] = {3, 7, 6, 5, 2, ALIGNMENT_CHAOTIC, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 1},
--Spider, Giant
[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},
--Squid, Giant
[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},
--Swashbuckler
[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},
--Tangle Vine
[366] = {20, 25, 1, 30, 1, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, {}, {}, {}, {}, 4},
--Troll
[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},
--Villager
[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},
--Wisp
[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},
--Wolf
[351] = {8, 16, 8, 8, 4, ALIGNMENT_CHAOTIC, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--TV Flower
[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},
--Skiff
[414] = {20, 20, 20, 20, 20, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 20},
--Raft
[415] = {10, 10, 10, 10, 10, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 10},
--Balloon
[423] = {10, 10, 10, 10, 10, ALIGNMENT_CHAOTIC, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 10},
--Cat
[388] = {4, 18, 8, 6, 2, ALIGNMENT_NEUTRAL, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0},
--Sinjen (Yew Stocks guy)
[263] = {11, 12, 12, 8, 4, ALIGNMENT_CHAOTIC, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}, 0}
}
monster_inv_food = { 129, 133, 128 } --portion of meat, ham, loaf of bread
function actor_put_to_sleep(actor)
if actor.obj_n == 431 then --horse with rider
Actor.use(actor) --dismount from horse
end
if actor.actor_num ~= 0 then
actor.asleep = true
if actor.in_party == true then
party_update_leader()
end
end
end
function actor_can_turn_invisible(obj_n)
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
return true
end
return false
end
-- returns true if defender loses the check
function actor_int_check(defender, attacker)
if is_god_mode_enabled() then
if defender.in_party then
return true
end
if attacker.in_party then
return false
end
end
if (math.floor(actor_int_adj(attacker) / 2) + 15) - actor_int_adj(defender) > math.random(1, 30) then
return true -- defender lost
end
return false
-- if((actor1_int_adj / 2 + 15) - actor2_int_adj > random(1,30))
--return
end
function subtract_movement_pts(actor, pts)
if actor.obj_n == 0x1af then pts = math.floor(pts / 2) end --if horse with rider
--fixme check with the original there are two timers that are checked but never seem to be used.
if pts < 1 then pts = 1 end
actor.mpts = actor.mpts - pts
if g_avatar_died == true then
actor_avatar_death()
end
end
function actor_move(actor, direction, flag)
----dgb("actor_move("..actor.name..", "..direction_string(direction)..", "..flag..") actor("..actor.x..","..actor.y..")\n");
local x,y,z = actor.x, actor.y, actor.z
if direction == DIR_NORTH then y = y - 1 end
if direction == DIR_SOUTH then y = y + 1 end
if direction == DIR_EAST then x = x + 1 end
if direction == DIR_WEST then x = x - 1 end
actor.direction = direction
local did_move = Actor.move(actor, x, y, z)
--actor.direction = direction
if did_move then
if actor.obj_n == 0x177 then slime_update_frames() end
subtract_map_movement_pts(actor)
----dgb("actor_move() did move actor("..actor.x..","..actor.y..")\n");
end
return did_move and 1 or 0
end
function actor_move_diagonal(actor, x_direction, y_direction)
local x,y,z = actor.x, actor.y, actor.z
local direction
if y_direction == DIR_NORTH then
y = y - 1
direction = x_direction == DIR_EAST and DIR_NORTHEAST or DIR_NORTHWEST
end
if y_direction == DIR_SOUTH then
y = y + 1
direction = x_direction == DIR_EAST and DIR_SOUTHEAST or DIR_SOUTHWEST
end
if x_direction == DIR_EAST then
x = x + 1
direction = y_direction == DIR_NORTH and DIR_NORTHEAST or DIR_SOUTHEAST
end
if x_direction == DIR_WEST then
x = x - 1
direction = y_direction == DIR_NORTH and DIR_NORTHWEST or DIR_SOUTHWEST
end
----dgb("actor_move_diagonal("..actor.name..", "..direction_string(direction)..")\n");
actor.direction = y_direction
local did_move = Actor.move(actor, x, y, z)
if did_move then
----dgb("did move\n");
if actor.obj_n == 0x177 then slime_update_frames() end
subtract_map_movement_pts(actor)
--dgb("set dir = "..direction_string(direction).." y_dir ="..direction_string(y_direction).." ")
end
return did_move and 1 or 0
end
function actor_map_dmg(actor, map_x, map_y, map_z)
local obj_n = actor.obj_n
local actor_type = actor_tbl[obj_n]
if actor.alive == false or actor.hit_flag == true then
return
end
if obj_n ~= 0x1a7 and obj_n ~= 0x19e and obj_n ~= 0x19f and obj_n ~= 0x19c --balloon, skiff, raft, ship, immune to map dmg
and (actor_type == nil or actor_type[13] == 0) and actor.protected == false and actor.in_vehicle == false then
local map_tile = map_get_dmg_tile_num(map_x, map_y, map_z)
if map_tile ~= nil then
actor_tile_dmg(actor, map_tile)
if map_tile == 732 then
local trap_obj = map_get_obj(map_x, map_y, map_z, 0xad) --trap
if trap_obj ~= nil then
trap_obj.invisible = false
end
end
end
end
end
function actor_tile_dmg(actor, map_tile)
if is_god_mode_enabled() and actor.in_party then
return
end
local obj_n = actor.obj_n
local actor_type = actor_tbl[actor.obj_n]
local dmg = 0
local random = math.random
if map_tile ~= nil then
actor.hit_flag = true
if map_tile == 564 then
--web
if obj_n ~= 0x169 then --giant spider
local str = actor_str_adj(actor)
if str < random(1, 0x1e) then
actor.paralyzed = true
print("`"..actor.name.." is stuck in a web!\n")
end
end
elseif (map_tile >= 2 and map_tile <= 5) or map_tile == 1165 then --swamp tiles or poison field.
--poison
local swamp_boots = Actor.inv_get_readied_obj_n(actor, 7) == 0x1c --swamp boots
if swamp_boots == false or map_tile == 1165 then
if (actor_type == nil or actor_type[19] == 0) and actor.poisoned == false then --19 immune to poison
actor.poisoned = true
print(actor.name.." poisoned!\n")
hit_anim(actor.x, actor.y)
end
end
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
--fire
if map_tile == 1164 then
dmg = random(1, 0x1e)
else
dmg = random(1, 8)
end
if actor_type ~= nil and actor_type[17] == 1 then --double damage from fire
dmg = dmg * 2
end
actor_hit(actor, dmg, nil)
elseif map_tile == 732 or map_tile == 1010 then
--trap, log saw
actor_hit(actor, random(1, 0x1e), nil)
elseif (map_tile >= 1458 and map_tile <= 1463) then
if obj_n ~= 0x16e then --tangle vine
actor_hit(actor, random(1, 8), nil)
end
elseif map_tile == 562 or map_tile == 731 then
--force field, spikes
actor_hit(actor, random(1, 8), nil)
elseif map_tile == 1167 then
--sleepfield
if (actor_type == nil or actor_type[21] == 0) and actor.asleep == false then --21 immune to sleep spell
actor_put_to_sleep(actor)
end
end
end
end
function actor_get_max_magic_points(actor)
local obj_n = actor.base_obj_n
if obj_n == 410 then --avatar
return actor.int * 2
end
if obj_n == 378 then --mage
return actor.int
end
if obj_n == 377 or obj_n == 386 then --swashbuckler, musician
return math.floor(actor.int/2)
end
return 0
end
function actor_str_adj(actor)
local str = actor.str
if actor.cursed == false then return str end
if str <= 3 then
return 1
end
return str - 3
end
function actor_dex_adj(actor)
local dex = actor.dex
if actor.cursed == true then
if dex <= 3 then
dex = 1
else
dex = dex - 3
end
end
if g_time_stopped == true or actor.asleep == true then
dex = 1
end
return dex
end
function actor_int_adj(actor)
local int = actor.int
if actor.cursed == true then
int = int - 3
end
if int < 1 then int = 1 end
return int
end
--
-- actor_combat_hit_check(attacker, foe, weapon_obj)
--
function actor_combat_hit_check(attacker, foe, weapon_obj)
if foe == nil then return false end
if is_god_mode_enabled() and attacker.in_party then
return true
end
if foe.luatype ~= "actor" or weapon_obj.obj_n == 48 then --48 glass sword
--dgb(foe.luatype)
return true
end
local use_str = false
local obj_n = weapon_obj.obj_n
if weapon_obj == attacker then
if actor_tbl[obj_n][16] == 1 then --actor uses strength
use_str = true
end
else
if obj_n == 4 or obj_n == 34 or obj_n == 39 or obj_n == 44 then --spiked helm, club, mace, two-handed hammer
use_str = true
end
end
local attack_role
if use_str then
attack_role = actor_str_adj(attacker)
else
attack_role = actor_dex_adj(attacker)
end
local foe_dex = actor_dex_adj(foe)
local roll = math.random(1,30)
--dgb("foe_dex = "..foe_dex.." attack_role = "..attack_role.." random(1,30) = "..roll.."\n\n")
if math.floor((foe_dex + 30 - attack_role) / 2) >= roll then
return false
end
return true
end
--
-- actor_init(actor)
--
function actor_init(actor, alignment)
local actor_base = actor_tbl[actor.obj_n]
if actor_base ~= nil then
actor.str = actor_randomise_stat(actor_base[1])
actor.dex = actor_randomise_stat(actor_base[2])
actor.int = actor_randomise_stat(actor_base[3])
local hp = actor_randomise_stat(actor_base[4])
actor.hp = hp
actor.level = math.floor((hp + 29) / 30)
actor.align = actor_base[6]
else
actor.str = 10
actor.dex = 10
actor.int = 10
actor.hp = 30
actor.level = 1
--actor.status = 32
end
if alignment ~= nil and alignment ~= ALIGNMENT_DEFAULT then
actor.align = alignment
end
actor.wt = 8
actor.combat_mode = 8
actor.magic = actor_get_max_magic_points(actor)
actor.mpts = actor.dex
actor.exp = 100
if actor.obj_n == 0x16d then --tangle vine pod. Add tangle vines.
local actor_x = actor.x
local actor_y = actor.y
local actor_z = actor.z
local a
if map_can_put(actor_x + 1, actor_y, actor_z) then
a = Actor.new(0x16e, actor_x + 1, actor_y, actor_z, actor.align, WT_TANGLE)
if a ~= nil then a.direction = DIR_EAST end
end
if map_can_put(actor_x - 1, actor_y, actor_z) then
a = Actor.new(0x16e, actor_x - 1, actor_y, actor_z, actor.align, WT_TANGLE)
if a ~= nil then a.direction = DIR_WEST end
end
if map_can_put(actor_x, actor_y + 1, actor_z) then
a = Actor.new(0x16e, actor_x, actor_y + 1, actor_z, actor.align, WT_TANGLE)
if a ~= nil then a.direction = DIR_SOUTH end
end
if map_can_put(actor_x, actor_y - 1, actor_z) then
a = Actor.new(0x16e, actor_x, actor_y - 1, actor_z, actor.align, WT_TANGLE)
if a ~= nil then a.direction = DIR_NORTH end
end
end
if actor_base ~= nil then
local obj,i,v,j,qty,chance,chest
--add spells
for i,v in ipairs(actor_base[22]) do
obj = Obj.new(336) --charge
obj.quality = v
obj.qty = 1
obj.status = 56
Actor.inv_add_obj(actor, obj)
end
--actor weapon
chance = 2
for i,v in ipairs(actor_base[23]) do
if v >= 36 and v <= 38 then --spear, throwing axe, dagger
qty = math.random(6, 12)
else
qty = 1
end
for j=1,qty do
obj = Obj.new(v)
obj.status = 57
if v == 90 then
obj.qty = 1
else
obj.qty = 0
end
Actor.inv_add_obj(actor, obj)
Actor.inv_ready_obj(actor, obj)
if v == 41 or v == 54 then --bow, magic bow
obj = Obj.new(55) --arrow
obj.status = 49
obj.qty = math.random(12, 24)
Actor.inv_add_obj(actor, obj)
elseif v == 42 or v == 50 then --crossbow, triple crossbow
obj = Obj.new(56) --bolt
obj.status = 49
obj.qty = math.random(12, 24)
Actor.inv_add_obj(actor, obj)
end
end
chance = chance * 2
end
-- actor armor
chance = 2
for i,v in ipairs(actor_base[24]) do
if math.random(1,chance) == 1 then
obj = Obj.new(v)
obj.status = 57
obj.qty = 1
obj.status = 56
Actor.inv_add_obj(actor, obj)
Actor.inv_ready_obj(actor, obj)
end
chance = chance * 2
end
--actor treasure
chance = 2
for i,v in ipairs(actor_base[25]) do
if math.random(1,chance) == 1 then
if v == 98 then --chest
chest = Obj.new(v)
chest.frame_n = 1
chest.status = 41
chest.qty = math.random(1, 100)
Actor.inv_add_obj(actor, chest)
if math.random(0, 1) == 1 then
obj = Obj.new(337) --effect
obj.status = 41
obj.qty = 1
obj.quality = 22
Obj.moveToCont(obj, chest)
end
if math.random(0, 3) ~= 0 then
obj = Obj.new(88) --gold coin
obj.status = 41
obj.qty = math.random(1, 100)
Obj.moveToCont(obj, chest)
end
if math.random(0, 3) ~= 0 then
obj = Obj.new(monster_inv_food[math.random(1, 3)])
obj.status = 41
obj.qty = math.random(1, 10)
Obj.moveToCont(obj, chest)
end
if math.random(0, 3) == 0 then
obj = Obj.new(90) --torch
obj.status = 41
obj.qty = math.random(1, 6)
Obj.moveToCont(obj, chest)
end
if math.random(0, 10) == 0 then
obj = Obj.new(77) --gem
obj.status = 41
obj.qty = math.random(1, 4)
Obj.moveToCont(obj, chest)
end
elseif v == 58 then --spell
if math.random(0, 3) ~= 0 and actor.obj_n ~= 362 then --Gargoyle
obj = Obj.new(88)
obj.status = 49
obj.qty = math.random(1, 100)
Actor.inv_add_obj(actor, obj)
end
if math.random(0, 3) == 0 then
obj = Obj.new(monster_inv_food[math.random(1, 3)])
obj.status = 49
obj.qty = math.random(1, 100)
Actor.inv_add_obj(actor, obj)
end
if math.random(0, 3) == 0 then
obj = Obj.new(275) --potion
obj.frame_n = math.random(0, 7) --random potion type
obj.status = 49
obj.qty = 1
Actor.inv_add_obj(actor, obj)
end
elseif v == 88 then --gold coin
obj = Obj.new(v)
obj.status = 49
obj.qty = math.random(1,30) + math.random(1, 30)
Actor.inv_add_obj(actor, obj)
else
obj = Obj.new(v)
obj.quality = 49
obj.qty = 1
Actor.inv_add_obj(actor, obj)
end
end
chance = chance * 2
end
end
end
-- [objectnum] = range
g_range_weapon_tbl = {
[40] = 2, -- morning star
[47] = 2, -- halberd
[38] = 3, -- dagger
[83] = 5, -- flask of oil
[33] = 4, -- sling
[37] = 3, -- throwing axe
[36] = 4, -- spear
[41] = 5, -- bow
[42] = 7, -- crossbow
[57] = 7, -- spellbook
[336] = 5, -- charge
[49] = 5, -- boomerang
[50] = 7, -- triple crossbow
[412] = 5, -- ship
[91] = 4, -- Zu Ylem
[54] = 7, -- magic bow
[79] = 7, -- lightning wand
[80] = 7, -- fire wand
}
local projectile_weapon_tbl =
{
--obj_n = {tile_num, initial_tile_rotation, speed, rotation_amount}
[33] = {398, 0, 2, 0}, -- sling
[36] = {547, 45,3, 0}, -- spear
[37] = {548, 0, 2, 10}, -- throwing axe
[38] = {549, 0, 2, 10}, -- dagger
[41] = {566, 90,4, 0}, -- bow
[42] = {567, 90,4, 0}, -- crossbow
[49] = {560, 0, 4, 10}, -- boomerang
[83] = {601, 0, 2, 0}, -- flask of oil
[50] = {567, 90,4, 0}, -- triple crossbow
[412] = {399, 0,2, 0}, -- ship
[91] = {612, 0, 2, 10}, -- Zu Ylem
[54] = {566, 90,4, 0}, -- magic bow
[79] = {392, 0, 2, 0}, -- lightning wand
[80] = {393, 0, 2, 0}, -- fire wand
}
weapon_dmg_tbl = {
[4] = 4, --spiked helm
[33] = 6, --sling
[34] = 8, --club
[35] = 8, --main gauche
[36] = 10, --spear
[37] = 10, --throwing axe
[38] = 6, --dagger
[39] = 15, --mace
[40] = 15, --morning star
[41] = 10, --bow
[42] = 12, --crossbow
[43] = 15, --sword
[44] = 20, --two-handed hammer
[45] = 20, --two-handed axe
[46] = 20, --two-handed sword
[47] = 30, --halberd
[49] = 8, --boomerang
[50] = 12, --triple crossbow
[48] = 255, --glass sword
[13] = 4, --spiked shield
[221] = 90, --cannon
[83] = 4, --flask of oil
[54] = 20, --magic bow
[109] = 2, --rolling pin
[100] = 6, --scythe
[101] = 4, --pitchfork
[103] = 4, --pick
[104] = 4, --shovel
[105] = 4, --hoe
[113] = 4, --cleaver
[114] = 4, --knife
[141] = 4, --decorative sword
[412] = 30, --ship
[91] = 0, --Zu Ylem
[78] = 4, --staff
[79] = 30, --lightning wand
[80] = 20, --fire wand
}
armour_tbl =
{
[1] = 1, --leather helm
[2] = 2, --chain coif
[3] = 3, --iron helm
[4] = 3, --spiked helm
[5] = 2, --winged helm
[6] = 2, --brass helm
[7] = 3, --Gargoyle Helm
[8] = 5, --magic helm
[9] = 2, --wooden shield
[10] = 3, --curved heater
[11] = 3, --winged shield
[12] = 3, --kite shield
[13] = 2, --spiked shield
[14] = 2, --black shield
[15] = 4, --door shield
[16] = 5, --magic shield
[17] = 1, --cloth armour
[18] = 2, --leather armor
[19] = 3, --ring mail
[20] = 4, --scale mail
[21] = 5, --chain mail
[22] = 7, --plate mail
[23] = 10, --magic armour
[35] = 1, --main gauche
[24] = 2, --spiked collar
[256] = 5, --protection ring
}
function get_weapon_dmg(weapon_obj_n)
local dmg = weapon_dmg_tbl[weapon_obj_n]
if dmg == nil and actor_tbl[weapon_obj_n] ~= nil then
dmg = actor_tbl[weapon_obj_n][5]
end
return dmg
end
function actor_get_ac(actor)
local ac = 0
local obj
for obj in actor_inventory(actor) do
if obj.readied then
local armour = armour_tbl[obj.obj_n]
if armour ~= nil then
ac = ac + armour
end
end
end
if actor.cursed == true then
ac = ac - 3
end
if actor.protected == true then
ac = ac + 3
end
return ac
end
function actor_select_obj_from_tbl(actor, obj_list_tbl)
local obj
local selected_obj = nil
local random = math.random
for obj in actor_inventory(actor) do
if obj_list_tbl[obj.obj_n] ~= nil then
if selected_obj == nil or random(0, 1) == 0 then
selected_obj = obj
end
end
end
return selected_obj
end
function acid_slug_dissolve_item(target_actor)
local acid_slug_items = { [0x2] = 1,
[0x3] = 1,
[0x4] = 1,
[0x5] = 1,
[0x7] = 1,
[0x0A] = 1,
[0x0B] = 1,
[0x0C] = 1,
[0x0D] = 1,
[0x0F] = 1,
[0x13] = 1,
[0x14] = 1,
[0x15] = 1,
[0x16] = 1,
[0x23] = 1,
[0x26] = 1,
[0x2B] = 1,
[0x71] = 1,
[0x72] = 1 }
local obj = actor_select_obj_from_tbl(target_actor, acid_slug_items)
if obj ~= nil then
play_sfx(SFX_SLUG_DISSOLVE, true)
print("A slug dissolves "..target_actor.name.."'s "..obj.name.."!\n")
Actor.inv_remove_obj(target_actor, obj)
obj = nil
end
end
function gremlin_steal_item(target_actor)
local gremlin_items = { [0x80] = 1,
[0x81] = 1,
[0x82] = 1,
[0x83] = 1,
[0x84] = 1,
[0x85] = 1,
[0x87] = 1,
[0xD1] = 1,
[0xD2] = 1,
[0xD3] = 1 }
local obj = actor_select_obj_from_tbl(target_actor, gremlin_items)
if obj ~= nil then
play_sfx(SFX_FAILURE, true)
print("`"..target_actor.name.." has been robbed!\n")
Actor.inv_remove_obj(target_actor, obj)
obj = nil
end
end
function actor_take_hit(attacker, defender, max_dmg)
if attacker.obj_n == 357 then --corpser
attacker.frame_n = 1 --reveal corpser.
end
if max_dmg == -1 then
max_dmg = 1
elseif max_dmg > 1 and max_dmg ~= 255 then
max_dmg = math.random(1, max_dmg)
end
local ac
local defender_type = defender.luatype
if defender_type == "actor" then
ac = actor_get_ac(defender)
if max_dmg ~= 255 and ac > 0 then
max_dmg = max_dmg - math.random(1, ac)
end
--dgb("\nac = "..ac.."\n")
else
ac = 0 --object
end
if is_god_mode_enabled() then
if defender_type == "actor" and defender.in_party then
-- print("`"..defender.name.." grazed.\n")
return
elseif attacker.in_party then
max_dmg = 255
end
end
if max_dmg > 0 then
if defender_type == "actor" and defender.wt > 1 and defender.wt < 16 then
--FIXME defender now targets attacker. I think.
end
local exp_gained = actor_hit(defender, max_dmg, attacker)
attacker.exp = attacker.exp + exp_gained
else
print("`"..defender.name.." grazed.\n")
end
if defender_type == "actor" then
actor_yell_for_help(attacker, defender, max_dmg)
local defender_obj_n = defender.obj_n
if defender.alive == true
or defender_obj_n == 0x1a7 --balloon
or defender_obj_n == 0x19e --skiff
or defender_obj_n == 0x19f --raft
or defender_obj_n == 0x19c then --ship
local attacker_obj_n = attacker.obj_n
if attacker_obj_n == 0x165 then --corpser
play_sfx(SFX_CORPSER_DRAGGED_UNDER, true)
print("`"..defender.name.." dragged under!\n")
defender.corpser_flag = true
if defender.in_party == true then
party_update_leader()
end
end
if attacker_obj_n == 0x16c then --acid slug
acid_slug_dissolve_item(defender)
end
if attacker_obj_n == 0x161 then --gremlin
gremlin_steal_item(defender)
end
local actor_type = actor_tbl[attacker_obj_n]
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.
defender.poisoned = true
print("`"..defender.name.." poisoned!\n")
end
if max_dmg > 0 then
actor_hit_msg(defender)
end
else
print("`"..defender.name.." killed!\n")
--FIXME do party roster change. maybe
end
end
end
function actor_hit(defender, max_dmg, attacker, no_hit_anim)
local defender_obj_n = defender.obj_n
local exp_gained = 0
if defender_obj_n == 0x1a7 then -- balloon.
return 0
end
if defender.luatype == "actor" then
if is_god_mode_enabled() then
if defender.in_party then
return 0
else
max_dmg = 255
end
end
--actor logic here
defender.hit_flag = true
if no_hit_anim == nil then
hit_anim(defender.x, defender.y)
end
if defender_obj_n == 409 then --lord british
defender.hp = 0xff
elseif defender_obj_n == 414 or defender_obj_n == 415 then --skiff, raft
-- hit random party member.
local num_in_party = party_get_size()
local party_member_num = 0
if num_in_party > 1 then
party_member_num = math.random(0, num_in_party - 1)
end
local rand_party_member = party_get_member(party_member_num)
actor_hit(rand_party_member, max_dmg, attacker, true)
actor_hit_msg(rand_party_member)
else
-- actor hit logic here
if max_dmg >= defender.hp then
if defender.hp ~= 0 then
if defender.in_party == true then player_subtract_karma(5) end --allowing your party to die is bad karma.
defender.hp = 0
-- defender.alive = false -- This doesn't work. The variable is get only
if alignment_is_evil(defender.align) then
local actor_base = actor_tbl[defender.obj_n]
if actor_base ~= nil then
--exp = (base_str + base_dex + base_int + base_hp + base_dmg + base_exp_gain) / 4
exp_gained = math.floor((actor_base[1] + actor_base[2] + actor_base[3] + actor_base[4] + actor_base[5] + actor_base[26]) / 4)
end
end
defender.visible = true
if defender.obj_n == 412 and defender.actor_num == 0 then --handle ship destruction
Actor.unlink_surrounding_objs(defender, true) --unlink ship front/back and make objects temporary.
defender.base_obj_n = 0x19f --raft
--try to toss raft out from center of the ship.
if actor_move(defender, DIR_NORTH, 1) == 0 then
if actor_move(defender, DIR_SOUTH, 1) == 0 then
if actor_move(defender, DIR_EAST, 1) == 0 then
actor_move(defender, DIR_WEST, 1)
end
end
end
defender.hp = 10
--defender.mpts = 1
party_move(defender.x, defender.y, defender.z)
else
actor_dead(defender)
defender.wt = WT_NOTHING
if defender.in_party == true and defender.in_vehicle == false then
party_update_leader()
end
end
end
else
defender.hp = defender.hp - max_dmg
if defender.wt == WT_FLEE then --flee
defender.wt = WT_ASSAULT --assault
end
if defender_obj_n == 375 then --slime
slime_divide(defender)
end
end
end
else
--object logic here
if defender_obj_n == 0x16e then -- tangle vine
--FIXME do something with tangle vine. -- I think the message is all that needed added
print("\nTangle vine killed!\n") -- The pod doesn't have this text in the original for some reason
elseif defender.stackable == false and defender.qty ~= 0 then
hit_anim(defender.x, defender.y)
if (defender_obj_n < 0x129 or defender_obj_n > 0x12c or defender.frame_n < 0xc) --check frame_n for door objects
and (defender_obj_n ~= 0x62 or defender.frame_n ~= 3) then --don't attack open chests
if defender.qty <= max_dmg then
print("\n`"..defender.name .. " broken!\n")
local child
for child in container_objs(defender) do -- look through container for effect object.
if child.obj_n == 0x151 then --effect
if attacker ~= nil then
actor_use_effect(attacker, child)
end
break
else
Obj.moveToMap(child, defender.x, defender.y, defender.z)
end
end
if defender_obj_n == 0x7b then --mirror
play_sfx(SFX_BROKEN_GLASS)
defender.frame_n = 2
player_subtract_karma(10)
else
map_remove_obj(defender)
end
else
defender.qty = defender.qty - max_dmg
end
end
end
end
return exp_gained
end
function actor_hit_msg(actor)
if actor.obj_n == 0x1a7 then return end --balloon
local hp = actor.hp
if hp == 0 then return end
local di = math.floor((hp * 4) / actor.max_hp)
local s
if di < 4 then
s = "\n`"..actor.name.." "
end
if di == 0 then
s = s.."critical!\n"
local wt = actor.wt
if actor_int_adj(actor) >= 5 and wt ~= WT_BERSERK and wt ~= WT_STATIONARY and wt > WT_FRONT then
actor.wt = WT_RETREAT
end
elseif di < 4 then
if di == 1 then
s = s.."heavily "
elseif di == 2 then
s = s.."lightly "
elseif di == 3 then
s = s.."barely "
end
s = s.."wounded.\n"
end
if s ~= nil then
print(s)
end
end
function actor_dead(actor)
local actor_base = actor_tbl[actor.obj_n]
if actor_base ~= nil then
if actor.obj_n == 0x163 then --gazer
Actor.new(0x157, actor.x, actor.y, actor.z) --insect
end
if actor_base[9] == 1 then --fades away. magical creature.
Actor.fade_out(actor, 20) --FIXME make fade speed configurable.
end
end
local in_vehicle = actor.in_vehicle
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
--add some blood.
--dgb("\nAdding Blood\n")
local blood_obj = Obj.new(0x152, math.random(0,2))
blood_obj.temporary = true
Obj.moveToMap(blood_obj, actor.x, actor.y, actor.z)
end
if actor.actor_num ~= 1 then --avatar
local create_body = true
if in_vehicle == true then
create_body = false -- don't drop body when party member dies on raft.
end
if actor_is_readiable_obj(actor) then
local animated_obj = Obj.new(actor.obj_n)
Obj.moveToMap(animated_obj, actor.x, actor.y, actor.z)
end
if actor.obj_n == 431 then --horse with rider
Actor.new(430, actor.x, actor.y, actor.z, ALIGNMENT_NEUTRAL, 12) --horse, wt wander
end
Actor.kill(actor, create_body)
else
actor.hp = 0 -- hackish way of making the Avatar not extinguish his torch
g_avatar_died = true
end
if actor.obj_n == 0x177 then
slime_update_frames()
end
end
function slime_divide(slime)
local random = math.random
local from_x, from_y, from_z = slime.x, slime.y, slime.z
local i
for i=1,8 do
local new_x = random(1, 2) + from_x - 1
local new_y = random(1, 2) + from_y - 1
if map_can_put(new_x, new_y, from_z) then
Actor.clone(slime, new_x, new_y, from_z)
slime_update_frames()
print("Slime divides!\n")
return
end
end
end
--[[
function bitor(x, y)
local p = 1
while p < x do p = p + p end
while p < y do p = p + p end
local z = 0
repeat
if p <= x or p <= y then
z = z + p
if p <= x then
x = x - p
end
if p <= y then y = y - p end
end
p = p * 0.5
until p < 1
return z
end
--]]
function slime_update_frames()
local i, j
local pow = math.pow
for i=1,0xff do
local actor = Actor.get(i)
if actor.obj_n == 0x177 and actor.alive then --slime
local new_frame_n = 0;
local idx = 0
for j = 1,8,2 do
local tmp_actor = map_get_actor(actor.x + movement_offset_x_tbl[j], actor.y + movement_offset_y_tbl[j], actor.z)
if tmp_actor ~= nil and tmp_actor.obj_n == 0x177 and tmp_actor.alive then
new_frame_n = new_frame_n + pow(2,idx)
end
idx = idx + 1
end
actor.frame_n = new_frame_n
end
end
end
function combat_range_check_target(actor_attacking)
if actor_attacking.wt > 1 and actor_attacking.wt < 0x10 then
--[[ FIXME
target = actor_attacking.target_obj
if target ~= actor_attacking then
if target.obj_n ~= 0 and target.alive == true and target.asleep == false and target.paralysed == false and target.corpser_flag == false then
if abs(actor_attacking.x - target.x) < 2 then
if abs(actor_attacking.y - target.y) < 2 and (time_stop_spell_timer == 0 or target.in_party == true) then
return false
end
end
end
end
--]]
end
return true
end
--check for arrows or bolts if attacking with a bow or crossbow
function out_of_ammo(attacker, weapon, print_message)
local weapon_obj_n = weapon.obj_n
if (weapon_obj_n == 41 or weapon_obj_n == 54) and Actor.inv_has_obj_n(attacker, 55) == false then --bow, magic bow, arrows
if(print_message) then
print("Out of arrows!\n")
end
return true --no arrows, bail out
end
if (weapon_obj_n == 42 or weapon_obj_n == 50) and Actor.inv_has_obj_n(attacker, 56) == false then --crossbow, triple crossbow, bolts
if(print_message) then
print("Out of bolts!\n")
end
return true --no bolts, bail out
end
return false
end
--
-- actor_attack(attacker, target, weapon)
--
function actor_attack(attacker, target_x, target_y, target_z, weapon, foe)
local random = math.random
local weapon_obj_n = weapon.obj_n
local weapon_quality = weapon.quality
if foe ~= nil and foe.actor_num == attacker.actor_num then
print("\n"..attacker.name.." is try to attack itself. Report me!\n") -- this shouldn't happen
return
end
if foe == nil then
foe = map_get_obj(target_x, target_y, target_z);
end
--dgb("\nactor_attack()\nrange = " .. get_attack_range(attacker.x,attacker.y, target_x, target_y).." weapon range="..get_weapon_range(weapon_obj_n))
if weapon_obj_n ~= attacker.obj_n then
--dgb("\nweapon = "..weapon.name.."obj_n = "..weapon_obj_n.."\n")
end
if Actor.get_range(attacker, target_x, target_y) > get_weapon_range(weapon_obj_n) then
return
end
--check for arrows or bolts if attacking with a bow or crossbow
if (out_of_ammo(attacker, weapon, false) == true) then
return --no arrows or bolts, bail out
end
local x_diff = target_x - attacker.x
local y_diff = target_y - attacker.y
local attacker_direction
if abs(x_diff) <= abs(y_diff) then
attacker_direction = (y_diff >= 0) and DIR_SOUTH or DIR_NORTH
else
attacker_direction = (x_diff >= 0) and DIR_EAST or DIR_WEST
end
local actor_obj_n = attacker.obj_n
--[[
if actor_obj_n == 0x19c then --ship
if(((*(&objlist_npc_movement_flags + actor_attacking) & 6 xor attacker_direction) & 2) == 0)
{
attacker_direction = attacker_direction xor 2;
}
else
{
attacker_direction = *(objlist_npc_movement_flags + actor_attacking) & 7;
}
end
--]]
if attacker.obj_n ~= 412 and actor_obj_n ~= 413 then --don't change direction for ship, silver serpent.
attacker.direction = attacker_direction
end
if actor_obj_n >= 0x170 and actor_obj_n <= 0x174 then -- skeleton, mongbat
local frame_n = attacker.frame_n
attacker.frame_n = (frame_n - (frame_n % 4)) + 2
end
local is_range_weapon = false
if weapon_obj_n ~= 40 and weapon_obj_n ~= 47 then -- morning star, halberd
if abs(x_diff) > 1 or abs(y_diff) > 1 or projectile_weapon_tbl[weapon_obj_n] ~= nil then
is_range_weapon = true
if combat_range_check_target(attacker) == false then return 1 end
end
end
local dmg = get_weapon_dmg(weapon_obj_n)
if dmg == nil then
dmg = 1
end
--FIXME run unknown func sub_1EABC here.
local player_loc = player_get_location()
if weapon_obj_n == 0x150 or weapon_obj_n == 0x39 then --charge, spellbook
if weapon_quality == 0x81 and actor_obj_n == 0x175 then -- special wisp magic (wisp teleport), wisp
if toss_actor(attacker, player_loc.x, player_loc.y, player_loc.z) == true then
print("Wisp teleports!\n")
end
return
end
local spell_retcode = 0
magic_cast_spell(weapon_quality, attacker, {x = target_x, y = target_y, z = target_z})
if weapon_quality == 0x50 and spell_retcode == 0xfe then
print("`"..foe.name .. " is charmed.\n")
elseif weapon_quality == 0x45 and spell_retcode == 0 then
print("`"..foe.name .. " is paralyzed.\n")
end
return
end
--weapon here.
local hit_actor = actor_combat_hit_check(attacker, foe, weapon)
local num_bolts
local missed_target = false
if is_range_weapon == true then
--FIXME might need to get new foe here.
target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
local failed_line_check
if foe ~= nil and (foe.x ~= target_x or foe.y ~= target_y) then
hit_actor = false
failed_line_check = true
else
failed_line_check = false
end
if hit_actor == false and failed_line_check == false then
if foe ~= nil then
target_x = target_x + random(0, 2) - 1
target_y = target_y + random(0, 2) - 1
local new_foe = map_get_actor(target_x, target_y, attacker.z)
if new_foe ~= foe then -- make sure multi-tile actors don't get attacked again on a miss
foe = new_foe
hit_actor = actor_combat_hit_check(attacker, foe, weapon)
end
end
end
if weapon_obj_n == 0x32 then --triple crossbow
num_bolts = Actor.inv_get_obj_total_qty(attacker, 0x38)
--dgb("total num_bolts = "..num_bolts.."\n")
if num_bolts > 3 then num_bolts = 3 end
end
if failed_line_check == false then
--FIXME might need to get new foe here.
target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
if foe ~= nil and (foe.x ~= target_x or foe.y ~= target_y) then
hit_actor = false -- leaving off line check bool since triple bolts should probably do damage
end
end
combat_range_weapon_1D5F9(attacker, target_x, target_y, target_z, foe, weapon)
else --standard weapon
if actor_find_max_xy_distance(attacker, player_loc.x, player_loc.y) < 6 then
--play_sound_effect(0x11, 0);
play_sfx(SFX_ATTACK_SWING, true)
end
end
if weapon_obj_n == 0x32 then --triple crossbow
local off = ((attacker.y - target_y + 5) * 11) + (attacker.x - target_x + 5)
local i
for i=1,num_bolts do
if i > 1 then
--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")
local t = g_projectile_offset_tbl[i-1][off+1]
foe = map_get_actor(target_x + movement_offset_x_tbl[t+1], target_y + movement_offset_y_tbl[t+1], player_loc.z)
--dgb("new_x = "..target_x + movement_offset_x_tbl[t+1].." new_y = "..target_y + movement_offset_y_tbl[t+1].."\n");
if failed_line_check == true then
hit_actor = false
else
target_x, target_y = map_line_hit_check(attacker.x, attacker.y, target_x, target_y, attacker.z)
if foe ~= nil and foe.x == target_x and foe.y == target_y then
hit_actor = actor_combat_hit_check(attacker, foe, weapon);
else
hit_actor = false
end
end
end
if hit_actor == true then
--dgb("triple xbow hit actor dmg = "..dmg.."\n");
actor_take_hit(attacker, foe, dmg);
if g_avatar_died == true then
return
end
end
end
else
if hit_actor == true then
local actor_base = actor_tbl[foe.obj_n]
if actor_base ~= nil and actor_base[14] == 1 -- takes half dmg
and weapon_obj_n ~= 0x30 and weapon_obj_n ~= 0x32 and weapon_obj_n ~= 0x36 then --glass sword, triple crossbow, magic bow
dmg = math.floor((dmg + 1) / 2)
end
if weapon_obj_n ~= 0x5b then --Zu Ylem
actor_take_hit(attacker, foe, dmg)
end
if weapon_obj_n == 0x30 then
print("Thy sword hath shattered!\n")
Actor.inv_remove_obj(attacker, weapon)
end
end
if weapon_obj_n == 0x31 then --boomerang
if attacker.x ~= 0 and attacker.y ~= 0 then --hack to stop return projectile if the avatar has died
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])
end
end
end
end
function combat_range_weapon_1D5F9(attacker, target_x, target_y, target_z, foe, weapon)
local weapon_obj_n = weapon.obj_n
local random = math.random
if weapon_obj_n == 0x32 then --triple cross bow
local index = ((attacker.y - target_y + 5) * 11) + (attacker.x - target_x + 5) + 1
local triple_crossbow_targets = {
{x=target_x,
y=target_y,
z=target_z},
{x=target_x + movement_offset_x_tbl[g_projectile_offset_tbl[1][index]+1],
y=target_y + movement_offset_y_tbl[g_projectile_offset_tbl[1][index]+1],
z=target_z},
{x=target_x + movement_offset_x_tbl[g_projectile_offset_tbl[2][index]+1],
y=target_y + movement_offset_y_tbl[g_projectile_offset_tbl[2][index]+1],
z=target_z}
}
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])
else
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])
end
if weapon_obj_n == 0x5b then
--Zu Ylem
if foe ~= nil and foe.obj_n ~= 0 then
Actor.inv_remove_obj_qty(attacker, 0x5b, 1)
spell_put_actor_to_sleep(attacker, foe)
end
elseif weapon_obj_n == 0x4f or weapon_obj_n == 0x50 then
--lightning wand, fire wand
if random(1, 100) == 37 then
Actor.inv_remove_obj(attacker, weapon)
print("A wand has vanished!\n")
end
elseif weapon_obj_n == 0x53 then
--flask of oil
Actor.inv_remove_obj_qty(attacker, 0x53, 1)
if map_is_water(target_x,target_y,target_z) == false then
local obj = Obj.new(317); --fire field
obj.temporary = true
Obj.moveToMap(obj, target_x, target_y, target_z)
end
elseif weapon_obj_n == 0x24 or weapon_obj_n == 0x25 or weapon_obj_n == 0x26 then
--spear, throwing axe, dagger
if Actor.inv_remove_obj_qty(attacker, weapon_obj_n, 1) == 1 and map_is_water(target_x,target_y,target_z) == false then
local obj = Obj.new(weapon_obj_n);
obj.ok_to_take = true
obj.temporary = true
Obj.moveToMap(obj, target_x, target_y, target_z)
end
elseif weapon_obj_n == 0x29 or weapon_obj_n == 0x2a or weapon_obj_n == 0x32 or weapon_obj_n == 0x36 then
--bow, crossbow, triple crossbow, magic bow
local projectile_obj = nil
if weapon_obj_n == 0x29 or weapon_obj_n == 0x36 then --bow, magic bow
projectile_obj = 0x37 --arrow
else
projectile_obj = 0x38 --bolt
end
local qty = 1
if weapon_obj_n == 0x32 then -- triple crossbow
qty = 3
end
Actor.inv_remove_obj_qty(attacker, projectile_obj, qty)
end
return 1
end
--
-- actor_get_weapon()
--
function actor_get_weapon(attacker, foe)
if foe == nil then return nil end
local in_party = attacker.in_party
local range = Actor.get_range(attacker, foe.x, foe.y)
--dgb("range = "..range.."\n")
local max_dmg = 0
local obj, weapon
for obj in actor_inventory(attacker) do
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)
--dgb("magic object quality = "..obj.quality.."\n");
return obj
else
if in_party == false or obj.readied == true then
local dmg = get_weapon_dmg(obj.obj_n)
if dmg ~= nil and dmg > max_dmg and get_weapon_range(obj.obj_n) >= range then
max_dmg = dmg
weapon = obj
end
end
end
end
if weapon == nil then --attack with bare hands.
weapon = attacker
end
--dgb("weapon: "..weapon.name.." dmg="..get_weapon_dmg(weapon.obj_n).."\n")
return weapon
end
--
-- actor_calculate_avg_coords()
--
function actor_calculate_avg_coords()
local n = 0
local avg_x = 0
local avg_y = 0
local player_loc = player_get_location()
local player_x = player_loc.x
local player_y = player_loc.y
local player = Actor.get_player_actor()
local player_dir = player.direction
local actor
for actor in party_members() do
if actor.wt ~= WT_FLANK and actor.wt ~= WT_BERSERK then
n = n + 1
avg_x = avg_x + actor.x
avg_y = avg_y + actor.y
end
end
if n > 0 then
party_avg_x = math.floor(avg_x / n)
party_avg_y = math.floor(avg_y / n)
else
party_avg_x = player_x
party_avg_y = player_y
end
n = 0
avg_x = 0
avg_y = 0
local i
for i=0,0xff do
actor = Actor.get(i)
if actor.obj_n ~= 0 and actor.alive then
if (actor.align == ALIGNMENT_EVIL or actor.align == ALIGNMENT_CHAOTIC) then
if actor.wt ~= WT_RETREAT or (abs(actor.x - player_x) <= 5 and abs(actor.y - player_y) <= 5) then
if abs(actor.x - player_x) < 0x18 and abs(actor.y - player_y) < 0x18 then
n = n + 1
avg_x = avg_x + actor.x
avg_y = avg_y + actor.y
end
end
end
end
end
wt_num_monsters_near = n
if n > 0 then
combat_avg_x = math.floor((avg_x + n / 2) / n)
combat_avg_y = math.floor((avg_y + n / 2) / n)
wt_rear_max_monster = nil
wt_rear_min_monster = nil
--wt_rear_max_party = nil
--wt_rear_min_party = nil
local tmp_x = combat_avg_x - party_avg_x
local tmp_y = combat_avg_y - party_avg_y
wt_front_target_actor = nil
local min_m_pos = 0
local max_m_pos = 0
local min_p_pos = 0
local max_p_pos = 0
--var_16 = 0x8000
local var_A
for i=0,0xff do
actor = Actor.get(i)
if actor.obj_n ~= 0 and actor.alive and (actor.wt == WT_FRONT or actor.wt == WT_PLAYER) then
--var_C = ((actor.x - party_avg_x) * tmp_x) + ((actor.y - party_avg_y) * tmp_y)
var_A = ((actor.x - party_avg_x) * tmp_y) - ((actor.y - party_avg_y) * tmp_x)
-- --dgb("ok "..actor.name.."\n")
if actor.in_party == false then
if wt_front_target_actor == nil then wt_front_target_actor = actor end
if var_A > max_m_pos then max_m_pos, wt_rear_max_monster = var_A, actor end
if var_A < min_m_pos then min_m_pos, wt_rear_min_monster = var_A, actor end
else
--dgb("yup var_A["..i.."] = "..var_A.."\n")
if var_A > max_p_pos then
--dgb("var_A > max_p_pos\n")
max_p_pos, wt_rear_max_party = var_A, actor
end
if var_A < min_p_pos then
--dgb("var_A < min_p_pos\n")
min_p_pos, wt_rear_min_party = var_A, actor
end
--if var_C > var_16 then var_16, unk_3DEAD = var_C, actor end
end
end
end
--FIXME I'm not really sure what the original does when these aren't set
if wt_rear_max_party == nil then wt_rear_max_party = player end
if wt_rear_min_party == nil then wt_rear_min_party = player end
else
--this is used by party members when in combat_front worktype.
combat_avg_x = player_x + movement_offset_x_tbl[(player_dir*2) + 1]
combat_avg_y = player_y + movement_offset_y_tbl[(player_dir*2) + 1]
end
if combat_avg_x == party_avg_x and combat_avg_y == party_avg_y then
combat_avg_x = combat_avg_x + movement_offset_x_tbl[(player_dir*2) + 1]
combat_avg_y = combat_avg_y + movement_offset_y_tbl[(player_dir*2) + 1]
end
--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")
end
--[[
function trace (event, line)
local s = debug.getinfo(2).name
if s == nil then
s = "Unk"
end
--io.stderr:write(s .. ":" .. line.."\n")
end
debug.sethook(trace, "l")
--]]
--
-- actor_update_all()
--
function actor_update_all()
--dgb("actor_update_all()\n")
if g_avatar_died == true then
actor_avatar_death()
return
end
g_time_stopped = is_time_stopped()
actor_calculate_avg_coords()
local actor
local selected_actor
repeat
selected_actor = nil
local di = 0
local dex_6 = 1
repeat
local player_loc = player_get_location()
local var_C = (player_loc.x - 16) - (player_loc.x - 16) % 8
local var_A = (player_loc.y - 16) - (player_loc.y - 16) % 8
for actor in party_members() do
if actor.wt == WT_FOLLOW and actor.mpts < 0 then
actor.mpts = 0
end
end
local player_z = player_loc.z
for i=0,0xff do
local actor = Actor.get(i)
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
if abs(actor.x - var_C) > 0x27 or abs(actor.y - var_A) > 0x27 then
if actor.wt >= 0x83 and actor.wt <= 0x86 then
--move actor to schedule location if it isn't on screen
local sched_loc = actor.sched_loc
if map_is_on_screen(sched_loc.x, sched_loc.y, sched_loc.z) == false then
Actor.move(actor, sched_loc.x, sched_loc.y, sched_loc.z)
actor_wt_walk_to_location(actor) --this will cancel the pathfinder and set the new worktype
subtract_movement_pts(actor, 10)
----dgb("\nActor SCHEDULE TELEPORT "..actor.actor_num.." to ("..sched_loc.x..","..sched_loc.y..","..sched_loc.z..")\n")
end
end
else
if actor.wt ~= WT_FOLLOW then
if actor.wt == 0x80 then
-- actor_set_worktype_from_schedule(actor)
actor.wt = actor.sched_wt
end
local dx = (actor.mpts * dex_6) - actor.dex * di
if actor.mpts >= actor.dex or dx > 0 or dx == 0 and actor.dex > dex_6 then
selected_actor = actor
di = actor.mpts
dex_6 = actor.dex
end
if actor.mpts >= actor.dex then
break
end
end
end
end
end
if di <= 0 then
advance_time(1)
end
until di > 0
if selected_actor.corpser_flag == true then
actor_corpser_regurgitation(selected_actor)
end
if selected_actor.corpser_flag == false then
if selected_actor.wt ~= WT_PLAYER and selected_actor.wt ~= WT_FOLLOW then
--dgb("perform_worktype("..selected_actor.name.."("..selected_actor.actor_num..") dex = "..selected_actor.dex.." mpts = "..selected_actor.mpts..").\n")
perform_worktype(selected_actor)
--sub_1C34A() --update map window??
if selected_actor.wt > 1 and selected_actor.wt < 0x10 then
--FIXME targetting?? do *(&objlist_ptr_unk_18f1 + actor_num) = actor_num
end
end
--sub_4726()
end
until selected_actor.obj_n ~= 0 and selected_actor.wt == WT_PLAYER and selected_actor.corpser_flag == false
if selected_actor ~= nil then --swap player to next party member with 'command' combat worktype.
local old_player = Actor.get_player_actor()
player_set_actor(selected_actor)
old_player.wt = WT_PLAYER --reset worktype to player as it gets changed to follow in Player::set_actor() :-(
end
if g_update_volcano == true then
local obj = find_volcano_near_player()
if obj ~= nil then
if obj.obj_n == 307 then --volcano
play_sfx(SFX_EARTH_QUAKE, false)
quake_start(1,200)
else
local temp_fumarole = Obj.new(obj) --duplicate the fumarole object so we can explode it.
explode_obj(temp_fumarole)
end
end
g_update_volcano = false
end
display_prompt(true)
end
function advance_time(num_turns)
local teleport = num_turns >= 60
--dgb("advance_time("..num_turns..")")
local time_stop_timer = timer_get(TIMER_TIME_STOP)
if time_stop_timer ~= 0 then
if time_stop_timer > num_turns then
timer_set(TIMER_TIME_STOP, time_stop_timer - num_turns)
return
end
timer_set(TIMER_TIME_STOP, 0)
end
timer_update_all(num_turns)
--update keg timer. explode kegs.
if g_keg_timer > 0 then
g_keg_timer = g_keg_timer - num_turns
if g_keg_timer <= 0 then
explode_keg()
g_keg_timer = 0
end
end
local random = math.random
local cloak_readied = false
local i
for i=0,0xff do
local actor = Actor.get(i)
if actor.obj_n ~= 0 then
if actor.mpts >= 0 then
m = actor.dex
else
m = actor.mpts + actor.dex
end
actor.mpts = m
if actor.wt == WT_FOLLOW and actor.corpser_flag == true then
actor_corpser_regurgitation(actor) --hack the original does this in party_move() but we don't have that in script yet.
end
if actor.in_party == true then
local obj
for obj in actor_inventory(actor) do
if obj.readied == true then
local obj_n = obj.obj_n
local obj_name = nil
if obj_n == 0x5a and obj.frame_n == 1 then --lit torch
if random(0, 1) == 0 then
if obj.qty > num_turns then
obj.qty = obj.qty - num_turns
else
--print("A torch burned out.\n")
Actor.inv_unready_obj(actor, obj)
end
end
elseif obj_n == 0x102 then --invisibility ring
if random(0, 1000) == 429 then
local two_rings = (Actor.inv_get_readied_obj_n(actor, 5) == 0x102
and Actor.inv_get_readied_obj_n(actor, 6) == 0x102)
if two_rings == false then
actor.visible = true
end
obj_name = "ring"
end
elseif obj_n == 0x101 then -- regeneration ring
--FIXME the original increments a counter here.
local hp = actor.hp + num_turns
local max_hp = actor.max_hp
if hp > max_hp then
hp = max_hp
end
actor.hp = hp
if random(0, 1000) == 734 then
obj_name = "ring"
end
elseif obj_n == 0x51 then --storm cloak
if random(0, 1000) == 588 then
obj_name = "cloak"
timer_set(TIMER_STORM, 0)
else
cloak_readied = true
end
end
if obj_name ~= nil then
Actor.inv_remove_obj(actor, obj)
print("A "..obj_name.." has vanished!\n")
end
end
end
end
local j
for j=1,num_turns do
actor_update_flags(actor)
end
actor_map_dmg(actor, actor.x, actor.y, actor.z)
actor.hit_flag = false
end
end
if cloak_readied == true then
timer_set(TIMER_STORM, 1)
end
local minute = clock_get_minute()
clock_inc(num_turns)
if minute + num_turns >= 60 then
update_actor_schedules(teleport)
--update magic points
local party_actor
for party_actor in party_members() do
local max_magic = actor_get_max_magic_points(party_actor)
if max_magic ~= 0 then -- Avatar, Mage, Swashbuckler, Musician
local magic_pts = party_actor.magic + party_actor.level
if magic_pts > max_magic then
magic_pts = max_magic
end
party_actor.magic = magic_pts
end
end
player_dec_alcohol(1)
end
if g_avatar_died == true then
actor_avatar_death()
end
if random(0,7) == 0 then
g_update_volcano = true
end
end
function actor_update_flags(actor)
local random = math.random
if actor.alive then
local var_8 = 0 --FIXME get proper value for var_8.
if actor.visible == false and var_8 == 0 and random(0, 0x3f) == 0
and Actor.inv_get_readied_obj_n(actor, 5) ~= 0x102 --invisibility rings
and Actor.inv_get_readied_obj_n(actor, 6) ~= 0x102 then
if actor_int_adj(actor) <= random(1, 0x1e) then
actor.visible = true
end
end
if actor.protected == true and random(0, 0x3f) == 0 then
if actor_int_adj(actor) <= random(1, 0x1e) then
actor.protected = false
end
end
if actor.cursed == true and random(0, 15) == 0 then
if actor_int_adj(actor) >= random(1, 0x1e) then
actor.cursed = false
end
end
if actor.charmed == true and random(0, 7) == 0 then
if actor_int_adj(actor) >= random(1, 0x1e) then
actor_remove_charm(actor)
end
end
if actor.paralyzed == true and random(0, 3) == 0 then
if actor_str_adj(actor) >= random(1, 0x1e) then
actor.paralyzed = false
party_update_leader()
end
end
if actor.asleep == true and actor.wt ~= WT_SLEEP and random(0, 15) == 0 then
actor.asleep = false
actor.hit_flag = true
actor.frame_n = actor.old_frame_n
if actor.in_party == true then
party_update_leader()
end
end
if actor.poisoned == true and actor.protected == false and random(0, 7) == 0 then
if map_is_on_screen(actor.x, actor.y, actor.z) == true then
actor_hit(actor, 1)
else -- don't show the animation or play the sfx
actor_hit(actor, 1, nil, true)
end
end
end
end
function actor_corpser_regurgitation(actor)
--dgb("actor_corpser_regurgitation("..actor.name..")\n");
if actor.corpser_flag == false then return end
if actor.wt == WT_PLAYER then
print("\n"..actor.name..":\nARGH!\n")
end
local random = math.random
local val = random(1, 0x1e)
if val < actor_str_adj(actor) then
play_sfx(SFX_CORPSER_REGURGITATE, true)
print("`"..actor.name.." regurgitated!\n")
actor.corpser_flag = false
if actor.in_party == true then
party_update_leader()
end
else
actor_hit(actor, random(1, 0xf))
actor.mpts = 0
end
end
function actor_remove_charm(actor)
if actor.charmed == true then
actor.charmed = false;
actor.align = actor.old_align
if actor.in_party == true then
actor.align = ALIGNMENT_GOOD
end
party_update_leader()
--[[ FIXME need to add functions to check for combat_mode and solo_mode
if party_in_combat_mode() then
actor.wt = combat_mode
else
if solo_mode() then
actor.wt = WT_NOTHING
else
actor.wt = WT_FOLLOW
end
end
--]]
return true
end
return false
end
function actor_yell_for_help(attacking_actor, defending_actor, dmg)
if defending_actor.hp == 0 then
return
end
if defending_actor.luatype == "actor" and defending_actor.align == ALIGNMENT_NEUTRAL and defending_actor.in_party == false then
local actor_base = actor_tbl[defending_actor.obj_n]
if attacking_actor.wt == WT_PLAYER and actor_base ~= nil then
if actor_base[7] ~= 0 and dmg > 0 then --[7] == can_talk
print("`"..defending_actor.name.." yells for help!\n")
activate_city_guards()
end
end
if defending_actor.wt >= 0x80 then
if attacking_actor.wt == WT_PLAYER then
if defending_actor.obj_n == 0x188 then -- musician
defending_actor.frame_n = defending_actor.old_frame_n
end
defending_actor.wt = WT_ATTACK_PARTY
end
else
if attacking_actor.align ~= ALIGNMENT_GOOD then
defending_actor.align = ALIGNMENT_GOOD
else
defending_actor.align = ALIGNMENT_CHAOTIC
end
if defending_actor.temp == true then
defending_actor.wt = WT_ASSAULT
end
end
end
end
function can_get_obj_override(obj) --meat, ribs, dead animal. These are toptile objects and need an override check.
if obj.obj_n == 209 or obj.obj_n == 210 or obj.obj_n == 211 then
return true
end
return false
end
function actor_get_obj(actor, obj)
if obj.getable == false then
print("\n\nNot possible.")
return false
end
if Actor.can_carry_obj_weight(actor, obj) == false then
print("\n\nThe total is too heavy.")
return false
end
local player_loc = player_get_location()
local i
local caught_stealing = false
if player_loc.z == 0 and obj.ok_to_take == false then --stealing logic only applies to surface level.
player_subtract_karma(1)
play_sfx(SFX_FAILURE, true)
for i=1,202 do --perm actors
local actor = Actor.get(i)
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
if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x6 then
print("\n\n\"Stop Thief!!!\"\n")
activate_city_guards()
caught_stealing = true
break
end
end
end
if caught_stealing == false then
print("\n\nStealing!!!\n")
end
end
obj.ok_to_take = true
Obj.moveToInv(obj, actor.actor_num)
subtract_movement_pts(actor, 3)
return true
end
function activate_city_guards()
--dgb("activate_city_guards()")
local i
local player_loc = player_get_location()
player_subtract_karma(5)
for i=1,0xff do
local actor = Actor.get(i)
if actor.alive == true and actor.z == player_loc.z and actor.in_party == false and actor.align == ALIGNMENT_NEUTRAL then
if actor.obj_n == 0x17e then -- guard
if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x20 then
actor.wt = WT_GUARD_ARREST_PLAYER
end
else
if actor.wt ~= WT_STATIONARY
and actor.wt ~= WT_NOTHING
and actor.wt ~= WT_PLAYER
and actor.paralyzed == false
and actor.asleep == false
and actor.corpser_flag == false then
if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) < 0x6 then
if actor.wt < 0x80 then
actor.wt = WT_UNK_13
else
if actor.wt > 0x86 then
if actor.obj_n == 0x188 then
actor.frame_n = actor.old_frame_n
end
actor.wt = WT_ATTACK_PARTY
end
end
end
end
end
end
end
end
function actor_catch_up_to_party(actor)
local target_actor = nil
local actor_x = actor.x
local actor_y = actor.y
local var_2 = 0x32
local party_actor
local random = math.random
for party_actor in party_members() do
if party_actor.alive == true and actor_ok_to_attack(actor, party_actor) == true then
local party_actor_x = party_actor.x
local party_actor_y = party_actor.y
local var_4 = (party_actor_x - actor_x) * (party_actor_x - actor_x) + (party_actor_y - actor_y) * (party_actor_y - actor_y)
if var_4 < var_2 or var_4 == var_2 and random(0, 1) ~= 0 then
var_2 = var_4
target_actor = party_actor
end
end
end
if target_actor == nil then
actor_move_towards_loc(actor, party_avg_x, party_avg_y)
return false
end
actor_move_towards_loc(actor, target_actor.x, target_actor.y)
if abs(target_actor.x - actor.x) > 1 or abs(target_actor.y - actor.y) > 1 then
return false
end
return true --reached party
end
function caught_by_guard(actor)
local lord_british = Actor.get(6)
--FIXME we don't want people going to jail before they answer the copy protection questions.
--if (objlist_talk_flags[5] & 0x80) == 0 then
-- actor.wt = 0x81
-- return
--end
Actor.show_portrait(actor)
print("\n\"Thou art under arrest!\"\n\n\"Wilt thou come quietly?\"\n\n:")
local var_6 = input_select("yn", false)
actor.wt = 0x81
Actor.hide_portrait()
if var_6 == "Y" then
print("es\n\nThe guard strikes thee unconscious!\n\nThou dost awaken to...\n")
--sub_2ACA1()
fade_out()
if party_is_in_combat_mode() then
party_set_combat_mode(false)
end
player_move(0xe7, 0xba, 0, true)
local cur_hour = clock_get_hour()
while cur_hour ~= 8 do
advance_time(60)
cur_hour = clock_get_hour()
--FIXME need to pause and update screen so player can see the sun-moon display move.
end
for party_actor in party_members() do
for var_4 in actor_inventory(party_actor, true) do -- recursively search containers in inventory.
--if((*(var_4 + objlist_obj_flags) & 0x18) == 0 || sub_CC5E(var_4, *(di + objlist_party_roster)) == 0)
-- break
if var_4.obj_n == 0x3f then --lockpick
Actor.inv_remove_obj(party_actor, var_4)
end
if var_4.obj_n == 0x40 and var_4.quality == 9 then --key
local obj = map_get_obj(0xeb, 0xb7, 0, 0xb1) --desk
if obj ~= nil then
Obj.moveToCont(var_4, obj)
end
end
end
end
local obj = map_get_obj(0xe7, 0xb8, 0, 0x12c) --steal door
if obj ~= nil then
obj.frame_n = 9 --close and lock door.
end
fade_in()
--[[
word_31D08 = 1
sub_E5F2()
music_play_song()
ax = sub_46DC()
--]]
else
print("o\n\n\"Then defend thyself, rogue!\"\n")
activate_city_guards()
actor.wt = WT_ASSAULT
actor.align = ALIGNMENT_EVIL
local i
for i=1,0xff do
local a = Actor.get(i)
if a.wt == WT_GUARD_ARREST_PLAYER then
a.wt = WT_ASSAULT
a.align = ALIGNMENT_EVIL
end
end
end
return
end
local tangle_vine_frame_n_tbl = {
1, 3, 1, 4,
5, 0, 4, 0,
1, 2, 1, 5,
2, 0, 3, 0
}
function move_tanglevine(actor, new_direction)
local actor_x = actor.x
local actor_y = actor.y
local old_direction = actor.direction
if new_direction ~= direction_reverse(old_direction) then
if actor_move(actor, new_direction, 1) ~= 0 then
local player_loc = player_get_location()
local tangle_obj = Obj.new(0x16e) --tanglevine
tangle_obj.frame_n = tangle_vine_frame_n_tbl[old_direction * 4 + new_direction + 1]
tangle_obj.status = 0x20
tangle_obj.quality = 0 --actor.id_n
Obj.moveToMap(tangle_obj, actor_x, actor_y, player_loc.z)
actor.frame_n = tangle_vine_frame_n_tbl[new_direction * 4 + math.random(0, 3) + 1]
return true
end
end
return false
end
function actor_wt_front(actor)
--dgb("actor_wt_front("..actor.name..")\n")
if actor_wt_front_1FB6E(actor) ~= 0 then
if actor.in_party == true then
actor_wt_attack(actor)
else
if actor_find_max_xy_distance(actor, party_avg_x, party_avg_y) >= 8 then
subtract_movement_pts(actor, 5)
else
if actor ~= wt_front_target_actor then
actor_wt_attack(actor)
else
if math.random(0, 7) ~= 0 then
actor_wt_attack(actor)
else
actor_move_towards_loc(actor, party_avg_x, party_avg_y)
end
end
end
end
return
end
return
end
function actor_wt_front_1FB6E(actor)
--dgb("actor_wt_front_1FB6E()\n")
if (wt_num_monsters_near == 0 or wt_front_target_actor == nil) and actor.in_party == false then
subtract_movement_pts(actor, 5)
return 1
end
local player_loc = player_get_location()
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
local actor_x = actor.x
local actor_y = actor.y
if actor.in_party == false then
centre_x = party_avg_x
centre_y = party_avg_y
diff_x = wt_front_target_actor.x - centre_x
diff_y = wt_front_target_actor.y - centre_y
else
centre_x = player_loc.x
centre_y = player_loc.y
diff_x = combat_avg_x - centre_x
diff_y = combat_avg_y - centre_y
end
--dgb("actor_wt_front_1FB6E() actor = ("..actor_x..","..actor_y..") centre = ("..centre_x..","..centre_y..") player = ("..player_loc.x..","..player_loc.y..")\n")
var_1E = (actor_x - centre_x) * diff_y - (actor_y - centre_y) * diff_x
if var_1E <= 0 then
var_24 = 0
else
var_24 = 1
end
if var_1E == 0 then
var_24 = math.random(0, 1)
end
if diff_y <= 0 then
var_14 = -1
else
var_14 = 1
end
if diff_x <= 0 then
var_12 = 1
else
var_12 = -1
end
if var_24 == 0 then
var_14 = -var_14
var_12 = -var_12
end
local tmp_actor
if actor.in_party == true then
tmp_actor = Actor.get_player_actor()
else
tmp_actor = wt_front_target_actor
end
--dgb("tmp_actor = "..tmp_actor.name.." at ("..tmp_actor.x..","..tmp_actor.y..")\n")
var_20 = (tmp_actor.x - centre_x) * diff_x + (tmp_actor.y - centre_y) * diff_y
if actor.in_party == true then
var_20 = var_20 + abs(diff_x) + abs(diff_y)
end
var_1C = diff_x * diff_x + diff_y * diff_y
if var_1C == 0 then
var_1C = 1
end
--dgb("getting target var_20 = "..var_20.." diff_x = "..diff_x.." diff_y = "..diff_y.." var_1C = "..var_1C.."\n")
target_x = math.floor((var_20 * diff_x) / var_1C) + centre_x
target_y = math.floor((var_20 * diff_y) / var_1C) + centre_y
unk_30A72 = 0
local chunk_x = player_loc.x - 16
chunk_x = chunk_x - (chunk_x % 8)
local chunk_y = player_loc.y - 16
chunk_y = chunk_y - (chunk_y % 8)
local found_actor = false
repeat
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
unk_30A72 = 1
--dgb("combat_front returned. too far away. actor=("..actor_x..","..actor_y..") target=("..target_x..","..target_y..") chunk=("..chunk_x..","..chunk_y..")\n")
return 1
end
if map_get_actor(target_x, target_y, player_loc.z) ~= nil then
found_actor = true
else
found_actor = false
end
if found_actor then
var_10 = (target_x + var_14 - centre_x) * diff_x + (target_y - centre_y) * diff_y
var_E = (target_x - centre_x) * diff_x + (target_y + var_12 - centre_y) * diff_y
if abs(var_10 - var_20) >= abs(var_E - var_20) then
target_y = target_y + var_12
else
target_x = target_x + var_14
end
end
until found_actor == false
local mpts = actor.mpts
if actor_move_towards_loc(actor, target_x, target_y) ~= 0 then
if actor.in_party == false or actor.x == target_x and actor.y == target_y then
return 0
end
if wt_num_monsters_near == 0 then
actor.mpts = mpts
end
if actor_move_towards_loc(actor, target_x, target_y) ~= 0 then
return 0
end
else
actor.mpts = mpts
end
actor_wt_attack(actor)
return 0
end
function actor_wt_rear(actor)
--dgb("actor_wt_rear()\n")
local var_C = 0
local player_loc = player_get_location()
local var_2,var_4,avg_y,avg_x,dx,ax,avg_x_diff, avg_y_diff
if actor.in_party == false then
if wt_num_monsters_near == 0 then subtract_movement_pts(actor, 5) return end
var_4 = wt_rear_min_monster
var_2 = wt_rear_max_monster
if var_4 == nil then var_4 = actor end
if var_2 == nil then var_2 = actor end
avg_x = combat_avg_x
avg_y = combat_avg_y
avg_x_diff = party_avg_x - combat_avg_x
avg_y_diff = party_avg_y - combat_avg_y
else
if wt_num_monsters_near == 0 then actor_move_towards_loc(actor, player_loc.x, player_loc.y) return end
var_4 = wt_rear_max_party
var_2 = wt_rear_min_party
avg_x = party_avg_x
avg_y = party_avg_y
avg_x_diff = combat_avg_x - party_avg_x
avg_y_diff = combat_avg_y - party_avg_y
end
local var_10 = 0x7fff
local align = actor.align
local i, var_12
for i=0,0xff do
local a = Actor.get(i)
if a.alive and a.wt == WT_FRONT and a.align == align then
var_12 = (a.x - avg_x) * avg_x_diff + (a.y - avg_y) * avg_y_diff
if var_12 < var_10 then var_10 = var_12 end
end
end
var_12 = (actor.x - avg_x) * avg_x_diff + (actor.y - avg_y) * avg_y_diff
local mpts = actor.mpts
local var_C
if actor.in_party == false or actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) <= 3 then
if var_12 < var_10 then
var_12 = (actor.x - avg_x) * avg_y_diff - (actor.y - avg_y) * avg_x_diff
dx = (var_4.x - avg_x) * avg_y_diff
if dx - (var_4.y - avg_y) * avg_x_diff >= var_12 then
dx = (var_2.x - avg_x) * avg_y_diff
ax = (var_2.y - avg_y) * avg_x_diff
if dx - ax <= var_12 then
var_C = 1
else
ax = (actor_move_towards_loc(actor, actor.x + avg_y_diff, actor.y - avg_x_diff) and -1 or 0) + 1
var_C = ax
end
else
ax = (actor_move_towards_loc(actor, actor.x - avg_y_diff, actor.y + avg_x_diff) and -1 or 0) + 1
var_C = ax
end
else
ax = (actor_move_towards_loc(actor, actor.x - avg_x_diff, actor.y - avg_y_diff) and -1 or 0) + 1
var_C = ax
end
else
ax = (actor_move_towards_loc(actor, player_loc.x, player_loc.y) and -1 or 0) + 1
var_C = ax
end
if var_C ~= 0 then
actor.mpts = mpts
actor_wt_attack(actor)
end
end
function actor_wt_flank(actor)
local player_loc = player_get_location()
local player_x = player_loc.x
local player_y = player_loc.y
local random = math.random
local abs = abs
if wt_num_monsters_near == 0 or actor.in_party == false and actor_find_max_xy_distance(actor, player_x, player_y) > 7 then
if actor.in_party == true and actor_find_max_xy_distance(actor, player_x, player_y) > 2 then
local mpts = actor.mpts
actor_move_towards_loc(actor, player_x, player_y)
actor.mpts = mpts
end
subtract_movement_pts(actor, 5)
return
end
local actor_align = actor.align
local actor_x = actor.x
local actor_y = actor.y
local tmp_x = combat_avg_x - party_avg_x
local tmp_y = combat_avg_y - party_avg_y
local var_20 = (actor_x - party_avg_x) * tmp_y - (actor_y - party_avg_y) * tmp_x
local var_1E, var_10, var_E
if var_20 <= 0 then
var_1E = 0
else
var_1E = 1
end
if var_20 == 0 then
var_1E = random(0, 1)
end
if var_1E == 0 then
var_10 = -tmp_y
var_E = tmp_x
var_20 = -var_20
else
var_10 = tmp_y
var_E = -tmp_x
end
local var_1A = -0x8000
local target_actor = nil
local i, tmp_actor
for i=1,0xff do
tmp_actor = Actor.get(i)
if tmp_actor ~= nil
and tmp_actor.alive == true
and tmp_actor.align ~= actor_align
and actor_ok_to_attack(actor, tmp_actor) == true
and (actor_align ~= ALIGNMENT_GOOD or alignment_is_evil(tmp_actor.align) == true)
and (actor_align ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_CHAOTIC) then
local target_x = tmp_actor.x
local target_y = tmp_actor.y
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)
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
local var_1C = (target_x - party_avg_x) * tmp_y - (target_y - party_avg_y) * tmp_x
if var_1E == 0 then
var_1C = -var_1C
end
if var_1C > var_1A then
var_1A = var_1C
target_actor = tmp_actor
end
end
end
end
if target_actor == nil then
actor_move_towards_player(actor)
return
end
g_obj = target_actor
tmp_x = target_actor.x
tmp_y = target_actor.y
local should_move_actor = false
local weapon_obj = actor_get_weapon(actor, target_actor)
local weapon_range = get_weapon_range(weapon_obj.obj_n)
local attack_range = Actor.get_range(actor, tmp_x, tmp_y)
if attack_range < 9 and attack_range <= weapon_range then
if map_can_reach_point(actor_x, actor_y, tmp_x, tmp_y, actor.z) == false then
if random(0, 1) == 0 then
tmp_x = target_actor.y - actor_y + actor_x
tmp_y = actor_y - target_actor.x - actor_x
else
tmp_x = actor_x - target_actor.y - actor_y
tmp_y = target_actor.x - actor_x + actor_y
end
should_move_actor = true
else
actor_attack(actor, tmp_x, tmp_y, actor.z, weapon_obj, target_actor)
subtract_movement_pts(actor, 10)
end
else
if var_10 <= 0 then
if var_10 < 0 then
tmp_x = tmp_x - 1
end
else
tmp_x = tmp_x + 1
end
if var_E <= 0 then
if var_E < 0 then
tmp_y = tmp_y - 1
end
else
tmp_y = tmp_y + 1
end
should_move_actor = true
end
if should_move_actor == true then
local mpts = actor.mpts
if actor_move_towards_loc(actor, tmp_x, tmp_y) == 0 then
actor.mpts = mpts
actor_wt_attack(actor)
end
end
return
end
function actor_wt_berserk(actor)
local target_actor = nil
local alignment = actor.align
local actor_x = actor.x
local actor_y = actor.y
local max_stats = 0
local i
for i=1,0xff do
local tmp_actor = Actor.get(i)
if tmp_actor ~= nil
and tmp_actor.alive == true
and tmp_actor.actor_num ~= actor.actor_num
and actor_find_max_xy_distance(tmp_actor, actor_x, actor_y) <= 8
and actor_ok_to_attack(actor, tmp_actor) == true
and (alignment ~= ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_EVIL)
and (alignment ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD)
and (alignment ~= ALIGNMENT_CHAOTIC or (tmp_actor.align ~= ALIGNMENT_CHAOTIC and tmp_actor.align ~= ALIGNMENT_NEUTRAL)) then
local combined_stats = tmp_actor.str + tmp_actor.dex + tmp_actor.int
if combined_stats > max_stats then
max_stats = combined_stats
target_actor = tmp_actor
end
end
end
if target_actor == nil then
actor_wt_attack(actor)
subtract_movement_pts(actor, 5)
return
end
local target_x = target_actor.x
local target_y = target_actor.y
local weapon_obj = actor_get_weapon(actor, target_actor)
local weapon_range = get_weapon_range(weapon_obj.obj_n)
local attack_range = Actor.get_range(actor, target_x, target_y)
if attack_range < 9 and attack_range <= weapon_range then
--g_obj = target_actor
if map_can_reach_point(actor_x, actor_y, target_x, target_y, actor.z) == false then
if math.random(0, 1) == 0 then
target_x = target_actor.y - actor_y + actor_x
target_y = actor_y - target_actor.x - actor_x
else
target_x = actor_x - target_actor.y - actor_y
target_y = target_actor.x - actor_x + actor_y
end
else
actor_attack(actor, target_x, target_y, actor.z, weapon_obj, target_actor)
subtract_movement_pts(actor, 15)
return
end
end
local mpts = actor.mpts
if actor_move_towards_loc(actor, target_x, target_y) == 0 then
actor.mpts= mpts
actor_wt_attack(actor)
end
subtract_movement_pts(actor, 5)
return
end
function actor_wt_guard_arrest_player(actor)
if actor_catch_up_to_party(actor) == true then
caught_by_guard(actor)
end
end
function actor_wt_combat_tanglevine(actor)
local random = math.random
local di
local abs = abs
local target = actor_find_target(actor)
if target ~= nil then
local target_x = target.x
local target_y = target.y
local actor_x = actor.x
local actor_y = actor.y
if abs(target_x - actor_x) < 2 and abs(target_y - actor_y) < 2 and random(0, 1) ~= 0 then
actor_attack(actor, target_x, target_y, actor.z, actor, target)
subtract_movement_pts(actor, 10)
return
end
if abs(target_x - actor_x) < 5 and abs(target_y - actor_y) < 5 and random(0, 3) == 0 then
target_x = target_x - actor_x
target_y = target_y - actor_y
if abs(target_x) <= abs(target_y) then
di = (target_y <= 0) and DIR_NORTH or DIR_SOUTH
if move_tanglevine(actor, di) == 0 then
di = (target_x <= 0) and DIR_WEST or DIR_EAST
move_tanglevine(actor, di)
return
end
else
di = (target_x <= 0) and DIR_WEST or DIR_EAST
if move_tanglevine(actor, di) == 0 then
di = (target_y <= 0) and DIR_NORTH or DIR_SOUTH
move_tanglevine(actor, di)
return
end
end
end
end
if random(0, 3) == 0 then
di = random(0, 3) --random direction north south east west
if actor.direction == di then
di = direction_reverse(di)
end
move_tanglevine(actor, di)
else
actor.mpts = 0
end
return
end
function actor_wt_combat_stationary(actor)
local rand = math.random
local align = actor.align
if align == ALIGNMENT_NEUTRAL then
subtract_movement_pts(actor, 5)
return
end
local i
for i=1,0x10 do
local target_x = rand(0, 8) + actor.x - 4
local target_y = rand(0, 8) + actor.y - 4
-- local g_obj = sub_1D351(actor, target_x, target_y)
local target_actor = map_get_actor(target_x, target_y, actor.z)
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
actor_attack(actor, target_x, target_y, actor.z, actor_get_weapon(actor, target_actor), target_actor)
subtract_movement_pts(actor, 10)
return
end
end
subtract_movement_pts(actor, 5)
return
end
function actor_wt_walk_straight(actor)
if math.random(0, 1) == 0 then subtract_movement_pts(actor, 5) return end
local wt = actor.wt
local sched = actor.sched_loc
local dir
if wt < WT_WALK_NORTH_SOUTH or actor.x ~= sched.x or actor.y ~= sched.y then
dir = actor.direction
else
if wt == WT_WALK_NORTH_SOUTH then dir = DIR_NORTH end
if wt == WT_WALK_EAST_WEST then dir = DIR_EAST end
if wt == WT_WALK_SOUTH_NORTH then dir = DIR_SOUTH end
if wt == WT_WALK_WEST_EAST then dir = DIR_WEST end
actor.direction = dir
end
local mpts = actor.mpts
if actor_move(actor, dir, 1) == 0 then
dir = direction_reverse(dir)
actor.mpts = mpts
actor_move(actor, dir, 1)
actor.direction = dir
end
end
function actor_wt_wander_around(actor)
local rand = math.random
if rand(0, 7) ~= 0 then subtract_movement_pts(actor, 5) return end
local random_wander_range = function ()
local i = 0
while rand(0, 1) ~= 0 do i = i + 1 end
if rand(0, 1) ~= 0 then i = -i end
return i
end
local abs=abs
local sched = actor.sched_loc
local sched_x_offset = actor.x - sched.x;
local sched_y_offset = actor.y - sched.y;
local direction
if abs(sched_y_offset) - abs(sched_x_offset) >= random_wander_range() then
direction = (random_wander_range() <= sched_y_offset) and DIR_NORTH or DIR_SOUTH
else
direction = (random_wander_range() <= sched_x_offset) and DIR_WEST or DIR_EAST
end
actor.direction = direction
actor_move(actor, direction, 1)
return
end
function actor_move_towards_player(actor)
local rand = math.random
if actor.wt ~= WT_STATIONARY then
if actor.in_party == true then
local player_loc = player_get_location()
local x,y = player_loc.x, player_loc.y
if actor.x ~= x and actor.y ~= y and rand(0, 3) == 0 then
actor_move_towards_loc(actor, x, y)
return
end
end
if rand(0, 7) == 0 then
actor_move(actor, rand(0, 3), 1)
return
end
end
subtract_movement_pts(actor, 5)
end
function actor_wt_attack(actor)
--dgb("actor_wt_attack()\n");
g_obj = actor_find_target(actor)
if g_obj ~= nil then
--dgb("target at ("..g_obj.x..","..g_obj.y..")\n")
end
local weapon_obj = actor_get_weapon(actor, g_obj)
if g_obj ~= nil then
local target_x = g_obj.x
local target_y = g_obj.y
local actor_x = actor.x
local actor_y = actor.y
local weapon_range = get_weapon_range(weapon_obj.obj_n)
if abs(target_x - actor_x) < 8 and abs(target_y - actor_y) < 8 and
Actor.get_range(actor, target_x, target_y) <= weapon_range then
if sub_1D59F(actor, target_x, target_y, weapon_range, 0) == true then
actor_attack(actor, g_obj.x, g_obj.y, g_obj.z, weapon_obj, g_obj)
subtract_movement_pts(actor, 10)
return
end
if math.random(0, 1) == 0 then
target_x = g_obj.y - actor_y + actor_x
target_y = actor_y - g_obj.x - actor_x
else
target_x = actor_x - g_obj.y - actor_y
target_y = g_obj.x - actor_x + actor_y
end
actor_move_towards_loc(actor, target_x, target_y)
return
end
if actor.wt ~= 3 then actor_move_towards_loc(actor, target_x, target_y) return end
subtract_movement_pts(actor, 5)
return
end
if actor.wt ~= 3 then actor_move_towards_player(actor) return end
subtract_movement_pts(actor, 5)
end
function actor_wt_timid(actor)
local player_loc = player_get_location()
if actor_find_max_xy_distance(actor, player_loc.x, player_loc.y) > 7 then
actor.mpts = 0
return
end
local target = actor_find_target(actor)
if target == nil then
actor_move_towards_player(actor)
return
end
local diff_x = target.x - actor.x
local diff_y = target.y - actor.y
local var_4
if actor.wt ~= WT_RETREAT then
var_4 = 3
else
var_4 = 8
end
if abs(diff_x) < var_4 and abs(diff_y) < var_4 then
local var_2 = actor.mpts
if actor_move_towards_loc(actor, actor.x - diff_x, actor.y - diff_y) == 0 then
--original logic -- actor.mpts = var_2
if actor.wt ~= WT_MOUSE and actor.wt ~= WT_FLEE then
actor.mpts = var_2 --moved here.
actor_wt_attack(actor)
end
end
else
if actor.align == ALIGNMENT_GOOD and actor.wt ~= WT_FLEE and actor.wt ~= WT_MOUSE then
actor_move_towards_loc(actor, party_avg_x, party_avg_y)
else
actor_move_towards_player(actor)
end
end
if actor.wt == WT_RETREAT then
if math.random(0, 3) == 0 then
if actor.level > actor.hp then
actor.hp = actor.hp + 1
end
if math.floor((actor.hp * 4) / actor.level) > 0 then
actor.wt = actor.combat_mode
end
end
end
end
function actor_wt_like(actor)
local actor_x = actor.x
local actor_y = actor.y
local party_actor
local random = math.random
for party_actor in party_members() do
if abs(party_actor.x - actor_x) < 3 and abs(party_actor.y - actor_y) < 3 then
if random(0, 1) == 0 then
actor_move_towards_loc(actor, party_avg_x, party_avg_y)
return
end
break
end
end
if random(0, 1) == 0 then
subtract_movement_pts(actor, 5)
else
if random(0, 1) == 0 then
if random(0, 1) == 0 then
actor_y = actor_y - 10
else
actor_y = actor_y + 10
end
else
if random(0, 1) == 0 then
actor_x = actor_x - 10
else
actor_x = actor_x + 10
end
end
actor_move_towards_loc(actor, actor_x, actor_y)
end
return
end
function actor_wt_unfriendly(actor)
local actor_x = actor.x
local actor_y = actor.y
local party_actor
local random = math.random
for party_actor in party_members() do
if abs(party_actor.x - actor_x) < 3 and abs(party_actor.y - actor_y) < 3 then
if random(0, 7) == 0 then
actor.wt = WT_ASSAULT
actor_wt_attack(actor)
return
end
break
end
end
if random(0, 1) == 0 then
subtract_movement_pts(actor, 5)
else
if random(0, 1) == 0 then
if random(0, 1) == 0 then
actor_y = actor_y - 10
else
actor_y = actor_y + 10
end
else
if random(0, 1) == 0 then
actor_x = actor_x - 10
else
actor_x = actor_x + 10
end
end
actor_move_towards_loc(actor, actor_x, actor_y)
end
return
end
function actor_wt_walk_to_location(actor)
Actor.walk_path(actor)
if Actor.is_at_scheduled_location(actor) then
actor.wt = actor.sched_wt
if actor.wt == WT_SLEEP then
-- add extinguish light source code if actors with a light source actually go to sleep
actor_wt_sleep(actor)
end
end
subtract_movement_pts(actor, 5) --FIXME what's the movement cost for pathfinding?
end
function actor_wt_sleep(actor)
--dgb("actor_wt_sleep("..actor.name..") at "..actor.x..","..actor.y..","..actor.z.."\n")
local obj = map_get_obj(actor.x, actor.y, actor.z, 0xa3) --bed
if obj ~= nil then
if obj.frame_n == 1 or obj.frame_n == 5 then --horizontal bed
actor.obj_n = 0x92 -- person sleeping
actor.frame_n = 0
else
if obj.frame_n == 7 or obj.frame_n == 10 then --vertical bed
actor.obj_n = 0x92 -- person sleeping
actor.frame_n = 1
end
end
end
end
function actor_wt_beg(actor)
local player_loc = player_get_location()
if actor.z == player_loc.z and abs(actor.x - player_loc.x) < 5 or abs(actor.y - player_loc.y) < 5 then
if actor_catch_up_to_party(actor) == true then
actor.wt = WT_WORK
Actor.talk(actor)
end
else
actor_wt_wander_around(actor)
end
end
function actor_wt_brawling(actor)
if math.random(0, 3) == 0 then
local target = actor_find_target(actor)
local player_loc = player_get_location()
if target ~= nil and actor_find_max_xy_distance(target, player_loc.x, player_loc.y) < 5 then
if actor_find_max_xy_distance(actor, target.x, target.y) < 2 then
hit_anim(target.x, target.y)
actor_move_towards_loc(actor, player_loc.x, player_loc.y)
else
actor_move_towards_loc(actor, target.x, target.y)
end
return
end
end
actor_wt_wander_around(actor)
end
wt_tbl = {
[WT_NOTHING] = {"WT_NOTHING", perform_worktype},
[WT_FRONT] = {"WT_FRONT", actor_wt_front},
[WT_REAR] = {"WT_REAR", actor_wt_rear},
[WT_FLANK] = {"WT_FLANK", actor_wt_flank},
[WT_BERSERK] = {"WT_BERSERK", actor_wt_berserk},
[WT_RETREAT] = {"WT_RETREAT", actor_wt_timid},
[WT_ASSAULT] = {"WT_ASSAULT", actor_wt_attack},
[WT_FLEE] = {"WT_FLEE", actor_wt_timid},
[WT_LIKE] = {"WT_LIKE", actor_wt_like},
[WT_UNFRIENDLY] = {"WT_UNFRIENDLY", actor_wt_unfriendly},
[WT_WANDER_NEAR_PLAYER] = {"WT_WANDER_NEAR_PLAYER", actor_move_towards_player},
[WT_TANGLE] = {"WT_TANGLE", actor_wt_combat_tanglevine},
[WT_STATIONARY] = {"WT_STATIONARY", actor_wt_combat_stationary},
[WT_GUARD_WALK_EAST_WEST] = {"WT_GUARD_WALK_EAST_WEST", actor_wt_walk_straight},
[WT_GUARD_WALK_NORTH_SOUTH] = {"WT_GUARD_WALK_NORTH_SOUTH", actor_wt_walk_straight},
--11
[WT_GUARD_ARREST_PLAYER] = {"WT_GUARD_ARREST_PLAYER", actor_wt_guard_arrest_player},
[WT_UNK_13] = {"WT_UNK_13", actor_wt_timid},
[WT_WALK_TO_LOCATION] = {"WT_WALK_TO_LOCATION", actor_wt_walk_to_location},
[WT_WALK_NORTH_SOUTH] = {"WT_WALK_NORTH_SOUTH", actor_wt_walk_straight},
[WT_WALK_EAST_WEST] = {"WT_WALK_EAST_WEST", actor_wt_walk_straight},
[WT_WALK_SOUTH_NORTH] = {"WT_WALK_SOUTH_NORTH", actor_wt_walk_straight},
[WT_WALK_WEST_EAST] = {"WT_WALK_WEST_EAST", actor_wt_walk_straight},
[WT_WANDER_AROUND] = {"WT_WANDER_AROUND", actor_move_towards_player},
[WT_WORK] = {"WT_WORK", actor_wt_wander_around},
[WT_UNK_94] = {"WT_UNK_94", actor_wt_wander_around},
[WT_BEG] = {"WT_BEG", actor_wt_beg},
[WT_ATTACK_PARTY] = {"WT_ATTACK_PARTY", actor_wt_attack},
[WT_BRAWLING] = {"WT_BRAWLING", actor_wt_brawling},
[WT_MOUSE] = {"WT_MOUSE", actor_wt_timid}
--[WT_] = {"WT_", actor_wt_rear}
}
function actor_ok_to_attack(actor, target_actor)
if target_actor.visible == false and (target_actor.in_party == false or actor.in_party == false) then return false end
if target_actor.z ~= actor.z then return false end
if target_actor.obj_n == 0x165 and target_actor.frame_n == 0 then return false end --corpser underground
if target_actor.corpser_flag == true then return false end
--FIXME need to check tileflag3 bit 4 is not set. The Ignore flag.
return true
end
function actor_find_target(actor)
local target_actor = nil
local align = actor.align
local actor_x = actor.x
local actor_y = actor.y
local var_2 = 0x7fff
local i
local player_loc = player_get_location()
local player_x = player_loc.x
local player_y = player_loc.y
for i=0,0xff do
local tmp_actor = Actor.get(i)
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
if actor.wt == WT_FLEE or
actor.wt == WT_MOUSE or
actor.wt == WT_UNK_13 or
actor.wt == WT_RETREAT or
actor.wt == WT_BRAWLING or
(align ~= ALIGNMENT_NEUTRAL or actor.wt == WT_ATTACK_PARTY and tmp_actor.align == ALIGNMENT_GOOD) and
(align ~= ALIGNMENT_CHAOTIC or tmp_actor.align ~= ALIGNMENT_CHAOTIC) and
tmp_actor.align ~= ALIGNMENT_NEUTRAL and
(align ~= ALIGNMENT_GOOD or alignment_is_evil(tmp_actor.align) == true) and
(align ~= ALIGNMENT_EVIL or tmp_actor.align == ALIGNMENT_GOOD or tmp_actor.align == ALIGNMENT_CHAOTIC) then
local target_x = tmp_actor.x
local target_y = tmp_actor.y
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
local var_6 = (target_x - actor_x)^2 + (target_y - actor_y)^2
if var_6 < var_2 or var_6 == var_2 and math.random(0, 1) ~= 0 then
var_2 = var_6
target_actor = tmp_actor
end
end
end
end
end
return target_actor
end
--FIXME this is a line of sight check for combat.
function sub_1D59F(actor, target_x, target_y, weapon_obj)
return map_can_reach_point(actor.x, actor.y, target_x, target_y, actor.z)
end
function perform_worktype(actor)
if g_time_stopped == true then
actor.mpts = 0
return
end
if wt_tbl[actor.wt] == nil then
subtract_movement_pts(actor, 5)
return
end
--dgb("wt = "..wt_tbl[actor.wt][1].."\n")
if actor.mpts > 0 then
local func = wt_tbl[actor.wt][2]
func(actor)
end
if actor.mpts == 0 then subtract_movement_pts(actor, 0xa) end
end
function spell_put_actor_to_sleep(attacker, foe)
--dgb("spell_put_actor_to_sleep("..attacker.name..",foe)\n")
local actor_base = actor_tbl[foe.obj_n]
if actor_base == nil or actor_base[21] == 0 then -- 21 is immune to sleep
if actor_int_check(foe, attacker) == false then
hit_anim(foe.x, foe.y)
actor_put_to_sleep(foe)
return 0xfe
else
return 1
end
end
return 2
end
function spell_poison_actor(attacker, foe)
local actor_base = actor_tbl[foe.obj_n]
if actor_base == nil or actor_base[19] == 1 or foe.actor_num == 0 then return 2 end --immune to poison
if math.floor((math.floor(actor_str_adj(foe) / 2) + 0x1e - actor_int_adj(attacker)) / 2) <= math.random(1, 0x1e) then
foe.poisoned = true
hit_anim(foe.x, foe.y)
return -1
end
return 1
end
function spell_take_fire_dmg(attacker, foe)
local actor_base = actor_tbl[foe.obj_n]
if actor_base == nil or actor_base[18] == 1 then return end --immune to magic
local dmg = math.random(1, 0x14)
if actor_base == nil or actor_base[17] == 1 then dmg = dmg * 2 end --double dmg from fire
local exp = actor_hit(foe, dmg)
if exp ~= 0 then
attacker.exp = attacker.exp + exp
end
actor_yell_for_help(attacker, foe, 1)
actor_hit_msg(foe)
end
function spell_charm_actor(attacker, foe)
if actor_int_check(foe, attacker) == true then return false end
if foe.charmed == true then
actor_remove_charm(foe)
else
foe.charmed = true
foe.old_align = foe.align
foe.align = attacker.align
hit_anim(foe.x, foe.y)
print("\n"..foe.name.." is charmed.\n")
if foe.in_party == true then
party_update_leader()
end
end
return true
end
function spell_kill_actor(attacker, foe)
local actor_base = actor_tbl[foe.obj_n]
if actor_base ~= nil and actor_base[20] == 1 then return 2 end --immune to corp spells
if actor_int_check(foe, attacker) == true then return 1 end
local exp = actor_hit(foe, foe.hp)
if exp ~= 0 then
attacker.exp = attacker.exp + exp
end
actor_yell_for_help(attacker, foe, 1)
actor_hit_msg(foe)
return -1
end
function spell_hit_actor(attacker, foe, spell_num)
if actor_int_check(foe, attacker) == true then return false end
local random = math.random
local dmg = 0
if spell_num == 5 then
dmg = random(1, 0xa)
elseif spell_num == 0x62 then
dmg = random(1, 0x1e)
elseif spell_num == 0x32 then
dmg = foe.hp - 1
end
print("\n")
local exp = actor_hit(foe, dmg)
if exp ~= 0 then
attacker.exp = attacker.exp + exp
end
actor_hit_msg(foe)
actor_yell_for_help(attacker, foe, 1)
return true
end
function actor_use_effect(actor, effect)
local random = math.random
local effect_type = random(0, 3)
Obj.removeFromEngine(effect) -- moved here so it won't be seen in chest
if effect_type == 0 then
print("Acid!\n")
actor_hit(actor, random(1, 0x14))
elseif effect_type == 1 then
print("Poison!\n")
actor.poisoned = true
hit_anim(actor.x, actor.y)
elseif effect_type == 2 then
print("Bomb!\n")
local hit_items = explosion(0x17e, actor.x, actor.y)
for k,v in pairs(hit_items) do
if v.luatype == "actor" then
actor_hit(v, random(1, 0x14))
end
end
elseif effect_type == 3 then
print("Gas!\n")
local hit_items = explosion(0x17c, actor.x, actor.y)
for k,v in pairs(hit_items) do
if v.luatype == "actor" then
v.poisoned = true
hit_anim(v.x, v.y)
end
end
end
end
function actor_resurrect(actor)
local karma = player_get_karma()
if karma < 98 then
actor.exp = math.floor((actor.exp * karma) / 100)
end
end
function get_LB_to_throne()
local cur_hour = clock_get_hour()
-- LB is on throne at 8-12 and 14-18
while cur_hour < 8 or cur_hour > 17 or (cur_hour > 11 and cur_hour < 14) do
advance_time(60)
cur_hour = clock_get_hour()
end
end
function actor_avatar_death()
--FIXME the hit tile is displayed constantly while the death tune is playing.
g_avatar_died = false -- before get_LB_to_throne()
local avatar = Actor.get(1)
if avatar.obj_n == 431 then --horse with rider
Actor.use(avatar) --dismount from horse
end
avatar.asleep = true --we do this so it looks like the avatar is dead.
print("\nAn unending darkness engulfs thee...\n\n")
play_sfx(SFX_AVATAR_DEATH, true)
fade_out()
print("A voice in the darkness intones, \"KAL LOR!\"\n")
play_sfx(SFX_KAL_LOR, true)
avatar.asleep = false
party_dismount_from_horses();
if g_armageddon == false then
party_resurrect_dead_members()
end
--unready inventory objects.
for obj in actor_inventory(avatar) do
if obj.readied and obj.obj_n ~= 76 then -- don't display message for amulet of submission
Actor.inv_unready_obj(avatar, obj)
end
end
party_heal() -- need to heal before player_move
party_update_leader()
player_move(0x133, 0x160, 0, true)
party_exit_vehicle(0x133, 0x160, 0)
get_LB_to_throne()
for i=0,0xff do
local actor = Actor.get(i)
actor.mpts = 0
if actor.temp == false and actor.wt == WT_ATTACK_PARTY then
actor.wt = WT_WALK_TO_LOCATION
end
end
avatar.mpts=1
actor_resurrect(avatar)
party_set_combat_mode(false)
party_set_party_mode()
fade_in()
end
--io.stderr:write("actor.lua loaded\n")