mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
1686 lines
50 KiB
Lua
1686 lines
50 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_SLEEP = 0x91
|
|
|
|
ACTOR_STAT_DMG = 7
|
|
--Actor stats table
|
|
--[obj_num] = {str,dex,int,hp,alignment,,damage??,,,,,,,,,,,,,,,,}
|
|
actor_tbl = {
|
|
--OBJ_YOURSELF5
|
|
[343] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_YOURSELF6
|
|
[344] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_POOR_MONK
|
|
[342] = {22,22,22,60,ALIGNMENT_EVIL,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_SCIENTIST
|
|
[345] = {15,15,25,30,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_YOUNG_WOMAN
|
|
[346] = {18,18,22,60,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_COWBOY
|
|
[347] = {20,25,18,60,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_GENTLEMAN
|
|
[348] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_UNIFORMED_MAN
|
|
[349] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_ADVENTURER
|
|
[350] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_COMMON_FELLOW
|
|
[351] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_MAN
|
|
[352] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_MAN_IN_WHITE
|
|
[353] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_LADY
|
|
[354] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_WOMAN
|
|
[355] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_ADVENTURESS
|
|
[356] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_METAL_WOMAN1
|
|
[357] = {25,25,25,90,ALIGNMENT_GOOD,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_MECHANICAL_MAN
|
|
[358] = {25,15,10,40,ALIGNMENT_GOOD,4,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_MARTIAN
|
|
[369] = {15,15,20,30,ALIGNMENT_NEUTRAL,0,6,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_SEXTELLEGER
|
|
[359] = {20,20,10,45,ALIGNMENT_CHAOTIC,4,35,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_BUSHALO
|
|
[360] = {25,12,11,35,ALIGNMENT_NEUTRAL,8,20,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
|
|
--OBJ_PLANTELLOPE
|
|
[361] = {15,22,9,20,ALIGNMENT_NEUTRAL,1,12,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_ROCKWORM2
|
|
[372] = {20,15,10,40,ALIGNMENT_CHAOTIC,8,25,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_WISP
|
|
[377] = {20,20,20,80,ALIGNMENT_NEUTRAL,3,30,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_AIRSQUID
|
|
[378] = {10,20,15,10,ALIGNMENT_EVIL,0,6,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0},
|
|
--OBJ_AIRSQUID1
|
|
[380] = {15,15,8,30,ALIGNMENT_EVIL,4,20,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_GIANT_MAW
|
|
[373] = {30,10,9,100,ALIGNMENT_CHAOTIC,3,30,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_TENTACLE1
|
|
[362] = {20,20,9,10,ALIGNMENT_CHAOTIC,2,20,1,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
|
|
--OBJ_CREEPING_CACTUS
|
|
[383] = {20,12,3,30,ALIGNMENT_CHAOTIC,4,25,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_AMMONOID
|
|
[363] = {15,15,3,20,ALIGNMENT_CHAOTIC,10,12,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_PROTO_MARTIAN
|
|
[364] = {20,19,10,20,ALIGNMENT_EVIL,2,10,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
|
|
--OBJ_MONSTER_FOOTPRINTS
|
|
[145] = {20,19,10,20,ALIGNMENT_EVIL,2,12,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
|
|
--OBJ_POD_DEVIL
|
|
[384] = {15,18,3,25,ALIGNMENT_CHAOTIC,4,12,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_JUMPING_BEAN
|
|
[385] = {10,22,10,10,ALIGNMENT_CHAOTIC,1,4,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_HEDGEHOG
|
|
[365] = {17,12,8,20,ALIGNMENT_CHAOTIC,3,20,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_CREEPER
|
|
[374] = {15,15,3,20,ALIGNMENT_CHAOTIC,1,16,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_AGROBOT
|
|
[376] = {29,18,10,30,ALIGNMENT_CHAOTIC,10,20,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
|
|
--OBJ_CANAL_WORM
|
|
[379] = {20,20,15,30,ALIGNMENT_CHAOTIC,5,25,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_DUST_DEVIL
|
|
[381] = {30,30,30,255,ALIGNMENT_NEUTRAL,99,60,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
--OBJ_SAND_TRAPPER
|
|
[386] = {25,25,10,40,ALIGNMENT_CHAOTIC,4,18,1,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0},
|
|
--OBJ_OXY_LEECH
|
|
[375] = {10,25,12,20,ALIGNMENT_CHAOTIC,1,10,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_CAVE_WORM
|
|
[366] = {15,15,15,30,ALIGNMENT_EVIL,2,15,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_JANITOR
|
|
[367] = {20,15,10,30,ALIGNMENT_CHAOTIC,10,20,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
|
|
--OBJ_BUSHRAT
|
|
[387] = {10,25,10,20,ALIGNMENT_EVIL,2,12,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_PLANTHER
|
|
[368] = {15,22,12,20,ALIGNMENT_CHAOTIC,4,20,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_MINOTAUR
|
|
[398] = {20,18,8,90,ALIGNMENT_NEUTRAL,2,15,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0},
|
|
--OBJ_YOUR_MOTHER
|
|
[391] = {12,20,15,255,ALIGNMENT_CHAOTIC,9,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0},
|
|
--OBJ_TREE
|
|
[408] = {25,18,5,90,ALIGNMENT_NEUTRAL,3,12,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_SOMETHING_YOU_SHOULDNT2
|
|
[424] = {25,18,5,90,ALIGNMENT_NEUTRAL,3,12,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
|
|
--OBJ_HUGE_RED_SPOT
|
|
[403] = {20,20,10,255,ALIGNMENT_EVIL,99,12,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
|
}
|
|
|
|
function is_actor_stat_bit_set(obj_n, bit_number)
|
|
local stats = actor_tbl[obj_n]
|
|
if stats ~= nil then
|
|
if stats[23 - bit_number] == 1 then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
g_party_is_warm = false
|
|
|
|
-- Berry logic
|
|
function actor_is_affected_by_purple_berries(actor_num)
|
|
|
|
if actor_num < 16 and timer_get(actor_num*3) > 0 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function actor_is_affected_by_green_berries(actor_num)
|
|
|
|
if actor_num < 16 and timer_get(actor_num*3+1) > 0 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function actor_is_affected_by_brown_berries(actor_num)
|
|
|
|
if actor_num < 16 and timer_get(actor_num*3+2) > 0 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function actor_get_purple_berry_count(actor_num)
|
|
if actor_num < 16 then
|
|
return timer_get(actor_num*3)
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
function actor_get_green_berry_count(actor_num)
|
|
if actor_num < 16 then
|
|
return timer_get(actor_num*3+1)
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
function actor_get_brown_berry_count(actor_num)
|
|
if actor_num < 16 then
|
|
return timer_get(actor_num*3+2)
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
function actor_decrement_berry_counter(actor, berry_type)
|
|
local actor_num = actor.actor_num
|
|
local count = timer_get(actor_num*3+berry_type)
|
|
if count > 0 and math.random(1, actor_int_adj(actor) * 4) == 1 then
|
|
timer_set(actor_num*3+berry_type, count - 1)
|
|
if (actor_num == 0 and g_in_dream_mode) or
|
|
(actor_num ~= 0 and g_in_dream_mode == false) then
|
|
play_md_sfx(0x32)
|
|
printl("A_PSYCHIC_POWER_FADES")
|
|
end
|
|
end
|
|
end
|
|
|
|
function actor_decrement_berry_counters(actor)
|
|
actor_decrement_berry_counter(actor, 0) --purple
|
|
actor_decrement_berry_counter(actor, 1) --green
|
|
actor_decrement_berry_counter(actor, 2) --brown
|
|
end
|
|
|
|
function actor_increment_berry_counter(actor_num, berry_type)
|
|
local count = timer_get(actor_num*3+berry_type)
|
|
if actor_num < 16 and count < 10 then
|
|
timer_set(actor_num*3+berry_type, count + 1)
|
|
end
|
|
end
|
|
|
|
function actor_increment_purple_berry_count(actor_num)
|
|
actor_increment_berry_counter(actor_num, 0)
|
|
end
|
|
|
|
function actor_increment_green_berry_count(actor_num)
|
|
actor_increment_berry_counter(actor_num, 1)
|
|
end
|
|
|
|
function actor_increment_brown_berry_count(actor_num)
|
|
actor_increment_berry_counter(actor_num, 2)
|
|
end
|
|
|
|
function actor_get_blue_berry_counter()
|
|
return timer_get(16*3)
|
|
end
|
|
|
|
function actor_set_blue_berry_counter(new_value)
|
|
timer_set(16*3, new_value)
|
|
end
|
|
|
|
function actor_clear_berry_counters(actor_num)
|
|
timer_set(actor_num*3, 0)
|
|
timer_set(actor_num*3+1, 0)
|
|
timer_set(actor_num*3+2, 0)
|
|
end
|
|
|
|
function actor_get_damage(actor)
|
|
local stat = actor_tbl[actor]
|
|
if stat ~= nil then
|
|
return stat[ACTOR_STAT_DMG]
|
|
end
|
|
|
|
return nil
|
|
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])
|
|
actor.hp = actor_randomise_stat(actor_base[4])
|
|
|
|
actor.level = 0
|
|
actor.align = actor_base[5]
|
|
else
|
|
actor.str = 15
|
|
actor.dex = 15
|
|
actor.int = 15
|
|
actor.hp = 30
|
|
actor.level = 1
|
|
actor.align = ALIGNMENT_CHAOTIC
|
|
end
|
|
|
|
if alignment ~= nil and alignment ~= ALIGNMENT_DEFAULT then
|
|
actor.align = alignment
|
|
end
|
|
|
|
actor.wt = 8
|
|
actor.combat_mode = 8
|
|
actor.mpts = actor.dex
|
|
actor.exp = 0
|
|
|
|
end
|
|
|
|
|
|
function subtract_map_movement_pts(actor)
|
|
local points = map_get_impedence(actor.x, actor.y, actor.z, false) + 5
|
|
subtract_movement_pts(actor, points)
|
|
end
|
|
|
|
function actor_divide(actor)
|
|
|
|
local random = math.random
|
|
local from_x, from_y, from_z = actor.x, actor.y, actor.z
|
|
|
|
for i=1,8 do
|
|
|
|
local new_x = wrap_coord(random(1, 2) + from_x - 1, from_z)
|
|
local new_y = wrap_coord(random(1, 2) + from_y - 1, from_z)
|
|
|
|
if map_can_put(new_x, new_y, from_z) then
|
|
|
|
--FIXME need to call sub_2C657 which I think updates the existing actor
|
|
Actor.clone(actor, new_x, new_y, from_z)
|
|
printfl("ACTOR_DIVIDES", actor.name)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
function defender_update_alignment(attacker, defender, damage)
|
|
if defender.luatype ~= "actor" then
|
|
return
|
|
end
|
|
|
|
if defender.align == ALIGNMENT_NEUTRAL and defender.in_party == false and (defender.wt <= 0x10 or defender.wt >= 0x80) then
|
|
if attacker.wt == WT_PLAYER and is_actor_stat_bit_set(defender.obj_n, 14) and damage > 0 then
|
|
printfl("ACTOR_ATTACKS", defender.name)
|
|
end
|
|
|
|
if defender.actor_num >= 0xc0 or defender.wt < 0x80 then
|
|
if attacker.align == ALIGNMENT_GOOD and defender.align ~= ALIGNMENT_EVIL and defender.wt ~= ALIGNMENT_CHAOTIC then
|
|
defender.align = ALIGNMENT_EVIL
|
|
end
|
|
|
|
if defender.wt >= 0xc0 then
|
|
defender.wt = 8
|
|
end
|
|
else
|
|
if attacker.wt == WT_PLAYER then
|
|
defender.wt = 0xa0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function canal_worm_eat_body(worm_actor)
|
|
--FIXME
|
|
--only eat if a body was created when the actor was killed.
|
|
end
|
|
|
|
function actor_handle_damage(defender)
|
|
if defender.obj_n == 370 or defender.hp == 0 then --OBJ_ROCKWORM_BASE
|
|
return
|
|
end
|
|
|
|
local hp_level = math.floor(((defender.hp * 4) / defender.max_hp))
|
|
if hp_level == 0 then
|
|
printfl("ACTOR_CRITICAL", defender.name)
|
|
--FIXME giant_maw check
|
|
local wt = defender.wt
|
|
if wt == 0xd then
|
|
defender.wt = 0x11
|
|
--FIXME if actor qaul ~= 0 then set actor.get(qual).wt = 0x11
|
|
elseif wt ~= 6 and wt ~= 0xe and wt ~= 2 and wt ~= 1 and wt ~= 0 and wt ~= 0x11 and wt ~= 0x19
|
|
and ( is_actor_stat_bit_set(defender.obj_n, 7) or is_actor_stat_bit_set(defender.obj_n, 14) ) then
|
|
if actor_int_adj(defender) >= 5 and defender.obj_n ~= 145 then --OBJ_MONSTER_FOOTPRINTS
|
|
defender.wt = 7
|
|
end
|
|
end
|
|
elseif hp_level < 4 then
|
|
local suffix
|
|
if is_actor_stat_bit_set(defender.obj_n, 14) and is_actor_stat_bit_set(defender.obj_n, 7) then
|
|
suffix = i18n("DAMAGED")
|
|
else
|
|
suffix = i18n("WOUNDED")
|
|
end
|
|
|
|
local damage_type
|
|
if hp_level == 1 then
|
|
damage_type = i18n("HEAVILY")
|
|
elseif hp_level == 2 then
|
|
damage_type = i18n("LIGHTLY")
|
|
elseif hp_level == 3 then
|
|
damage_type = i18n("BARELY")
|
|
end
|
|
printfl("ACTOR_HIT_MESSAGE", defender.name, damage_type, suffix)
|
|
|
|
end
|
|
end
|
|
|
|
function actor_take_hit(attacker, defender, max_dmg, damage_mode)
|
|
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 armour_value = 0
|
|
if damage_mode ~= 2 then
|
|
if defender.luatype == "actor" then
|
|
armour_value = actor_get_ac(defender)
|
|
elseif defender.obj_n == 388 and defender.frame_n == 0 then --OBJ_GLOW_WORM
|
|
armour_value = 2
|
|
end
|
|
end
|
|
|
|
if armour_value > 0 and max_dmg < 255 then
|
|
max_dmg = max_dmg - math.random(1, armour_value)
|
|
end
|
|
|
|
if max_dmg > 0 then
|
|
if (attacker.align == ALIGNMENT_EVIL or attacker.align == ALIGNMENT_CHAOTIC)
|
|
and defender.luatype == "actor" and defender.in_party
|
|
and party_is_in_combat_mode() == false
|
|
and player_is_in_solo_mode() == false then
|
|
party_set_combat_mode(true)
|
|
end
|
|
|
|
attacker.exp = attacker.exp + actor_hit(defender, max_dmg, damage_mode)
|
|
else
|
|
--FIXME call either sub_19088 or sub_18F7D
|
|
printfl("ACTOR_GRAZED", defender.name)
|
|
end
|
|
|
|
if defender.luatype == "actor" then
|
|
--FIXME only call if defender == selected obj
|
|
defender_update_alignment(attacker, defender, max_dmg)
|
|
if defender.hp == 0 then
|
|
if attacker.obj_n == 379 then --OBJ_CANAL_WORM
|
|
canal_worm_eat_body(attacker)
|
|
end
|
|
else
|
|
if is_actor_stat_bit_set(attacker.obj_n, 2) and math.random(0, 3) == 0 and actor_immune_to_dmg(defender) == false then
|
|
printfl("ACTOR_PARALYZED", defender.name)
|
|
defender.paralyzed = true
|
|
end
|
|
|
|
if defender.alive and max_dmg > 0 then
|
|
actor_handle_damage(defender)
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function actor_immune_to_dmg(actor)
|
|
local actor_num = actor.actor_num
|
|
local obj_n = actor.obj_n
|
|
if obj_n == 381 or obj_n == 390 or obj_n == 391 or obj_n == 358 or obj_n == 382 then
|
|
return true
|
|
end
|
|
|
|
if actor_num == 0x5c or actor_num == 0x5d or actor_num == 0x59 or actor_num == 0x5a or actor_num == 0x54 or actor_num == 0x52
|
|
or actor_num == 0x6d or actor_num == 0x68 or actor_num == 0x67 or actor_num == 0x69 or actor_num == 0x77 or actor_num == 0x78
|
|
or actor_num == 0x40 or actor_num == 0x3c then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function actor_print_custom_hit_message(actor)
|
|
local actor_num = actor.actor_num
|
|
if actor_num == 0x69 then
|
|
printfl("ACTOR_CRITICAL", Actor.get(0x19).name)
|
|
elseif actor_num == 0x3c then
|
|
printfl("ACTOR_CRITICAL", actor.name)
|
|
elseif actor_num == 0x52 then
|
|
printfl("ACTOR_HIT_MESSAGE", actor.name, i18n("HEAVILY"), i18n("WOUNDED"))
|
|
elseif actor_num == 0x68 then
|
|
if Actor.get_talk_flag(actor, 2) then
|
|
printfl("ACTOR_CRITICAL", actor.name)
|
|
else
|
|
printfl("ACTOR_HIT_MESSAGE", actor.name, i18n("HEAVILY"), i18n("WOUNDED"))
|
|
end
|
|
Actor.set_talk_flag(actor, 5)
|
|
Actor.talk(actor)
|
|
else
|
|
printl("IT_HAS_NO_EFFECT")
|
|
end
|
|
end
|
|
|
|
local RED_HIT_TILE = 257
|
|
local BLUE_HIT_TILE = 258
|
|
|
|
function hit_target(target, hit_tile)
|
|
if map_is_on_screen(target.xyz) then
|
|
if target.luatype == "actor" and target.in_party then
|
|
play_md_sfx(1, PLAY_ASYNC)
|
|
else
|
|
play_md_sfx(2, PLAY_ASYNC)
|
|
end
|
|
hit_anim(target.x, target.y) --FIXME need to apply hit colour tile param here.
|
|
end
|
|
|
|
if target.luatype == "actor" then
|
|
--FIXME
|
|
-- les bx, objlist_unk_1af1_ptr
|
|
-- mov al, es:[bx+si]
|
|
-- or al, 8
|
|
-- mov es:[bx+si], al
|
|
|
|
if target.asleep then
|
|
target.asleep = false
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- Hit an actor or object
|
|
function actor_hit(defender, max_dmg, damage_mode)
|
|
|
|
|
|
local defender_obj_n = defender.obj_n
|
|
local exp_gained = 0
|
|
local player_loc = player_get_location()
|
|
|
|
if defender.z ~= player_loc.z or max_dmg < 0 then
|
|
return 0
|
|
end
|
|
|
|
if defender.luatype == "actor" then
|
|
print("actor_hit("..defender.actor_num..")\n")
|
|
|
|
if defender.actor_num == 0 and defender.hp <= max_dmg and g_current_dream_stage == 0xe0 then
|
|
max_dmg = defender.hp - 1
|
|
end
|
|
|
|
if damage_mode == 1 then
|
|
hit_target(defender, BLUE_HIT_TILE)
|
|
else
|
|
hit_target(defender, RED_HIT_TILE)
|
|
end
|
|
|
|
if actor_immune_to_dmg(defender) then
|
|
actor_print_custom_hit_message(defender)
|
|
defender.hp = 0xff
|
|
if defender_obj_n == 391 then --OBJ_YOUR_MOTHER
|
|
local gender_title = ""
|
|
if player_get_gender() == 0 then
|
|
gender_title = i18n("MAN")
|
|
else
|
|
gender_title = i18n("WOMAN")
|
|
end
|
|
printfl("HOW_DARE_YOU_YOUNG_PERSON", gender_title)
|
|
actor_take_hit(defender, Actor.get(0), 45, 0)
|
|
actor_take_hit(defender, Actor.get(0), 45, 0)
|
|
actor_take_hit(defender, Actor.get(0), 45, 0)
|
|
elseif defender.actor_num == 0x6d or defender.actor_num == 0x67 then
|
|
Actor.set_talk_flag(Actor.get(0x6b), 3)
|
|
local dream_actor = Actor.get(0)
|
|
Actor.move(dream_actor, g_prev_player_x, g_prev_player_y, dream_actor.z)
|
|
elseif defender.actor_num == 0x40 then --Rasputin
|
|
--FIXME attack_rasputin()
|
|
end
|
|
else
|
|
if damage_mode == 3 and is_actor_stat_bit_set(defender_obj_n, 3) then
|
|
printl("IT_HAS_NO_EFFECT")
|
|
return 0
|
|
end
|
|
if is_actor_stat_bit_set(defender_obj_n, 4) then
|
|
max_dmg = max_dmg * 2
|
|
end
|
|
|
|
if (damage_mode == 0 and actor_num ~= 0) or g_current_dream_stage ~= 0xe0 then --or word_41184 ~= 0xe0
|
|
if math.random(6, 0x64) <= max_dmg then
|
|
--FIXME actor_add_blood()
|
|
end
|
|
local hp = defender.hp
|
|
if hp <= max_dmg then
|
|
--FIXME exp_gained = kill_actor()
|
|
else
|
|
defender.hp = hp - max_dmg
|
|
|
|
if defender.wt == 9 or defender.wt == 11 then
|
|
defender.wt = 8
|
|
elseif defender.wt == 10 or defender.wt == 12 then
|
|
defender.wt = 0x1b
|
|
end
|
|
|
|
if is_actor_stat_bit_set(defender_obj_n, 6) then
|
|
actor_divide(defender)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
actor_hit_obj(defender, max_dmg, damage_mode)
|
|
end
|
|
|
|
return exp_gained
|
|
end
|
|
|
|
function attack_bucket(bucket, damage_mode)
|
|
if damage_mode == 3 and bucket.frame_n == 2 then
|
|
printl("THE_ICE_THAWS")
|
|
bucket.frame_n = 1
|
|
elseif damage_mode == 1 and bucket.frame_n == 1 then
|
|
printl("THE_WATER_FREEZES")
|
|
bucket.frame_n = 2
|
|
end
|
|
end
|
|
|
|
function actor_hit_obj(obj, dmg, damage_mode)
|
|
if obj.qty == 0 or not is_obj_attackable(obj) then
|
|
if obj.obj_n == 160 then --OBJ_BUCKET
|
|
attack_bucket(obj, damage_mode)
|
|
end
|
|
return
|
|
end
|
|
|
|
if damage_mode == 0 or damage_mode == 3 or is_plant_obj(obj) then
|
|
|
|
if damage_mode == 1 then
|
|
hit_target(obj, BLUE_HIT_TILE)
|
|
else
|
|
hit_target(obj, RED_HIT_TILE)
|
|
end
|
|
|
|
if damage_mode == 3 and is_obj_burnable(obj) then
|
|
dmg = obj.qty
|
|
end
|
|
|
|
if obj.qty <= dmg then
|
|
if obj.obj_n == 217 then --OBJ_GLASS_PITCHER
|
|
play_md_sfx(0x1a)
|
|
obj.obj_n = 218 --OBJ_BROKEN_CRYSTAL
|
|
obj.frame_n = 0
|
|
if g_in_dream_mode and g_current_dream_stage == 0x44 then
|
|
local pitcher
|
|
for tmp_obj in find_obj_from_area(0x21, 0x33, 2, 0x1c, 0x10) do
|
|
if tmp_obj.obj_n == 217 then --OBJ_GLASS_PITCHER
|
|
pitcher = tmp_obj
|
|
end
|
|
end
|
|
if pitcher == nil then
|
|
Actor.set_talk_flag(0x54, 6)
|
|
end
|
|
end
|
|
else
|
|
play_md_sfx(0)
|
|
Obj.removeFromEngine(obj)
|
|
end
|
|
else
|
|
if obj.obj_n == 307 then --OBJ_DEVIL_POD
|
|
printl("THE_POD_SPLITS_OPEN")
|
|
local pod_devil = Actor.new(384, obj.x, obj.y, obj.z)
|
|
|
|
actor_init(pod_devil)
|
|
Actor.move(pod_devil, obj.x, obj.y, obj.z)
|
|
Obj.removeFromEngine(obj)
|
|
else
|
|
if obj.obj_n == 388 then --OBJ_GLOW_WORM
|
|
obj.frame_n = 0
|
|
obj.quality = math.random(0xb, 0x12)
|
|
play_md_sfx(0x22)
|
|
end
|
|
obj.qty = obj.qty - dmg
|
|
end
|
|
|
|
end
|
|
|
|
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
|
|
|
|
if actor.obj_n ~= 382 then --COKER FIXME need to have an update frame function here.
|
|
actor.direction = direction
|
|
end
|
|
|
|
local did_move = Actor.move(actor, x, y, z)
|
|
|
|
--FIXME need more logic here.
|
|
--footprints, bots etc.
|
|
|
|
if did_move then
|
|
subtract_map_movement_pts(actor)
|
|
----dgb("actor_move() did move actor("..actor.x..","..actor.y..")\n");
|
|
end
|
|
|
|
return did_move
|
|
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
|
|
subtract_map_movement_pts(actor)
|
|
end
|
|
|
|
return did_move and 1 or 0
|
|
end
|
|
|
|
function actor_update_frame(actor, direction)
|
|
local obj_n = actor.obj_n
|
|
|
|
--actor.direction = direction
|
|
if obj_n >= 342 or obj_n <= 358 then --human actors
|
|
--print("actor: "..actor.actor_num.."("..actor.x..","..actor.y..","..actor.z..")")
|
|
for obj in objs_at_loc(actor.x, actor.y, actor.z) do
|
|
if obj ~= nil then
|
|
local tmp_obj_n = obj.obj_n
|
|
--print("actor:"..actor.actor_num.." obj: " .. tmp_obj_n)
|
|
if tmp_obj_n == 406 or (tmp_obj_n == 407) or tmp_obj_n == 216 or tmp_obj_n == 289 then
|
|
--sit here
|
|
if tmp_obj_n == 406 then -- OBJ_BENCH
|
|
actor.frame_n = 3
|
|
elseif tmp_obj_n == 407 then -- OBJ_COUCH
|
|
actor.frame_n = 4 + 3
|
|
elseif tmp_obj_n == 289 then -- OBJ_DREAM_MACHINE2
|
|
actor.frame_n = 8 + 3
|
|
else
|
|
actor.frame_n = math.floor(direction/2) * 4 + 3
|
|
end
|
|
|
|
return
|
|
end
|
|
end
|
|
end
|
|
--standing
|
|
|
|
end
|
|
end
|
|
|
|
function revive_avatar()
|
|
if g_in_dream_mode == true then
|
|
--FIXME died in dream world. old_player_x == 0
|
|
--FIXME respawn into dreamworld
|
|
else
|
|
-- normal avatar death
|
|
avatar_falls_unconscious()
|
|
end
|
|
end
|
|
|
|
function actor_resurrect(actor)
|
|
--FIXME do we need to do anything here?
|
|
end
|
|
|
|
function avatar_falls_unconscious()
|
|
printl("OVERCOME_BY_YOUR_WOUNDS_YOU_FALL_UNCONSCIOUS")
|
|
|
|
fade_out()
|
|
|
|
local input
|
|
while input == nil do
|
|
--canvas_update()
|
|
input = input_poll()
|
|
if input ~= nil then
|
|
break
|
|
end
|
|
end
|
|
|
|
local location
|
|
local target
|
|
if Actor.get_talk_flag(0x46, 3) then
|
|
location = i18n("THE_OLYMPUS_MINE")
|
|
target={x=0xa4,y=0xc3,z=4}
|
|
else
|
|
location = i18n("THE_SPACE_CAPSULE")
|
|
target={x=0x19d,y=0x278,z=0}
|
|
end
|
|
|
|
printfl("YOU_AWAKEN_BACK_AT_FEELING_RESTORED", location)
|
|
|
|
input_select(nil, true)
|
|
|
|
party_resurrect_dead_members()
|
|
|
|
for actor in party_members() do
|
|
actor.hp = actor.max_hp
|
|
actor.mpts = actor_dex_adj(actor)
|
|
end
|
|
|
|
party_move(target)
|
|
local hour = clock_get_hour()
|
|
local minutes = clock_get_minute()
|
|
minutes = 60 - minutes
|
|
if hour < 7 then
|
|
hour = 6 - hour
|
|
else
|
|
hour = 24 - (hour + 1) + 7
|
|
end
|
|
|
|
clock_inc(hour * 60 + minutes + math.random(0,59)) --advance time to between 7am and 8am on the next day
|
|
update_watch_tile()
|
|
advance_time(0)
|
|
|
|
party_update_leader()
|
|
party_set_combat_mode(false)
|
|
party_set_party_mode()
|
|
|
|
local blood = Actor.get(0x12)
|
|
if blood.alive then
|
|
Actor.set_talk_flag(blood, 5)
|
|
Actor.talk(blood)
|
|
end
|
|
|
|
fade_in()
|
|
end
|
|
|
|
function party_update()
|
|
local avatar = Actor.get(1)
|
|
if avatar.hp == 0 or (g_in_dream_mode == true and Actor.get(0).alive == false) then
|
|
if g_in_dream_mode == true then
|
|
g_objlist_1d22_unk = 0
|
|
if g_prev_player_x == 0 then
|
|
printl("YOU_SHAKE_YOURSELF_AWAKE_FROM_THE_NIGHTMARE")
|
|
wake_from_dream()
|
|
else
|
|
local avatar = Actor.get(1)
|
|
local dream_actor = Actor.get(0)
|
|
dream_actor.hp = avatar.max_hp
|
|
dream_actor.poisoned = false
|
|
Actor.move(dream_actor, g_prev_player_x, g_prev_player_y, dream_actor.z)
|
|
party_set_combat_mode(false)
|
|
printl("YOU_FEEL_YOUR_DREAM_CONSCIOUSNESS_RETURNING")
|
|
if g_current_dream_stage == 0xc0 then
|
|
if not Actor.get_talk_flag(0x66, 2)
|
|
and Actor.get_talk_flag(0x66, 3)
|
|
and Actor.get_talk_flag(0x66, 4)
|
|
and not Actor.get_talk_flag(0x66, 5)
|
|
then
|
|
for i=0,0xff do
|
|
local maw = Actor.get(i)
|
|
if maw.obj_n == 373 and maw.wt == 0x12 and maw.x == 0x87 and maw.y == 0x17 then
|
|
Actor.kill(maw, false)
|
|
end
|
|
end
|
|
if map_get_obj(0x7f, 0x18, dream_actor.z, 224) == nil then --OBJ_BRIDGE
|
|
local bridge = Obj.new(224, 3)
|
|
Obj.moveToMap(bridge, 0x7f, 0x18, dream_actor.z)
|
|
end
|
|
end
|
|
Actor.set_talk_flag(0x66, 7)
|
|
local raxachk = Actor.get(0x66)
|
|
Actor.talk(raxachk)
|
|
end
|
|
end
|
|
else
|
|
revive_avatar()
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function actor_update_all()
|
|
party_update()
|
|
--pathfinding here.
|
|
|
|
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)
|
|
--FIXME need to check 11000b not zero on obj_flags
|
|
if actor.obj_n ~= 0 and actor.z == player_z and actor.mpts > 0 and actor.paralyzed == false and (actor.asleep == false or actor.wt == 0x80) and actor.wt ~= WT_NOTHING and actor.alive == true then
|
|
--FIXME need to check map wrapping here.
|
|
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 dex_adjusted = actor_dex_adj(actor)
|
|
local dx = (actor.mpts * dex_6) - dex_adjusted * di
|
|
if actor.mpts >= dex_adjusted or dx > 0 or dx == 0 and dex_adjusted > dex_6 then
|
|
selected_actor = actor
|
|
di = actor.mpts
|
|
dex_6 = dex_adjusted
|
|
end
|
|
|
|
if dex_adjusted <= actor.mpts then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if di <= 0 then
|
|
for i=0,0xff do
|
|
local actor = Actor.get(i)
|
|
local dex_adjusted = actor_dex_adj(actor)
|
|
if actor.mpts >= 0 then
|
|
actor.mpts = dex_adjusted
|
|
else
|
|
actor.mpts = actor.mpts + dex_adjusted
|
|
end
|
|
end
|
|
advance_time(1)
|
|
end
|
|
|
|
until di > 0
|
|
|
|
if selected_actor.wt ~= WT_PLAYER and selected_actor.wt ~= WT_FOLLOW then
|
|
--print("perform_worktype("..selected_actor.name.."("..selected_actor.actor_num..") dex = "..selected_actor.dex.." mpts = "..selected_actor.mpts..").\n")
|
|
perform_worktype(selected_actor)
|
|
party_update()
|
|
if selected_actor.wt > 1 and selected_actor.wt < 0x1b then
|
|
--FIXME targetting?? do *(&bjlist_unk_19f1_ptr + actor_num) = actor_num
|
|
end
|
|
end
|
|
|
|
until selected_actor.obj_n ~= 0 and selected_actor.wt == WT_PLAYER
|
|
|
|
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
|
|
|
|
|
|
display_prompt(true)
|
|
end
|
|
|
|
|
|
|
|
local projectile_weapon_tbl = --FIXME weed sprayer and spray gun
|
|
{
|
|
--obj_n = {tile_num, initial_tile_rotation, speed, rotation_amount} --FIXME: all untested
|
|
[40] = {575, 90,4, 0}, -- Cupid's bow and arrows
|
|
[41] = {259, 90,4, 0}, -- derringer --FIXME: tile_num, rot, speed, amount
|
|
[42] = {259, 90,4, 0}, -- revolver --FIXME: rot, speed, amount
|
|
[43] = {262, 90,4, 0}, -- shotgun --FIXME: rot, speed, amount
|
|
[44] = {259, 90,4, 0}, -- rifle --FIXME: rot, speed, amount
|
|
[45] = {259, 90,4, 0}, -- Belgian combine rifle --FIXME: rot, speed, amount (has 3 modes)
|
|
--[45] = {262, 90,4, 0}, -- Belgian combine spread(has two) --FIXME: rot, speed, amount
|
|
[46] = {259, 90,4, 0}, -- elephant gun --FIXME: tile_num, rot, speed, amount
|
|
[47] = {398, 0, 2, 0}, -- sling --FIXME: tile_num, rot, speed, amount
|
|
[48] = {575, 90,4, 0}, -- bow
|
|
[241] = {334, 90,4, 0}, -- heat ray gun --FIXME: rot, speed, amount (has 3 modes)
|
|
[242] = {335, 90,4, 0}, -- freeze ray gun --FIXME: rot, speed, amount (has 3 modes)
|
|
[313] = {267, 90,4, 0}, -- M60 machine gun --FIXME: rot, speed, amount (if even used)
|
|
|
|
}
|
|
|
|
armour_tbl =
|
|
{
|
|
--[1] = 0, --cap
|
|
--[2] = 0, --cowboy hat
|
|
[3] = 1, --pith helmet
|
|
[4] = 2, --military helmet
|
|
--[5] = 0, --derby
|
|
--[6] = 0, --kerchief
|
|
--[7] = 0, --silk scarf
|
|
--[8] = 0, --muffler
|
|
--[9] = 0, --man's shoes
|
|
--[10] = 0, --woman's shoes
|
|
[11] = 1, --riding boots
|
|
--[12] = 0, --ruby slippers
|
|
[13] = 2, --thigh boots
|
|
[14] = 3, --hip boots
|
|
--[15] = 0, --winged shoes
|
|
--[17] = 0, --cloth jacket
|
|
--[18] = 0, --wool sweater
|
|
--[19] = 0, --cape
|
|
--[20] = 0, --duster
|
|
[21] = 1, --wool overcoat
|
|
[22] = 2, --sheepskin jacket
|
|
[23] = 2, --arctic parka
|
|
--[25] = 0, --cotton pants
|
|
--[26] = 0, --cotton dress
|
|
[27] = 1, --denim jeans
|
|
--[28] = 0, --wool pants
|
|
--[29] = 0, --wool dress
|
|
[30] = 3, --chaps and jeans
|
|
--[31] = 0, --man's shoes
|
|
--[33] = 0, --lady's silk gloves
|
|
[34] = 1, --driving gloves
|
|
--[35] = 0, --cotton work gloves
|
|
[36] = 2, --work gloves
|
|
--[37] = 0, --wool mittens
|
|
[38] = 1, --rubber gloves
|
|
[39] = 3, --welding gloves
|
|
--[90] = 0, --electric belt?
|
|
--[234] = 0, --martian jewelry
|
|
}
|
|
|
|
function actor_get_ac(actor)
|
|
local ac = 0
|
|
|
|
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
|
|
return ac
|
|
end
|
|
|
|
local clothing_warmth_tbl = {
|
|
[1] = 1, -- OBJ_CAP
|
|
[2] = 2, -- OBJ_COWBOY_HAT
|
|
[3] = 3, -- OBJ_PITH_HELMET
|
|
[4] = 3, -- OBJ_MILITARY_HELMET
|
|
[5] = 2, -- OBJ_DERBY
|
|
[6] = 1, -- OBJ_KERCHIEF
|
|
[7] = 1, -- OBJ_SILK_SCARF
|
|
[8] = 2, -- OBJ_MUFFLER
|
|
[9] = 1, -- OBJ_MANS_SHOES
|
|
[10] = 1, -- OBJ_WOMANS_SHOES
|
|
[11] = 2, -- OBJ_RIDING_BOOTS
|
|
[14] = 4, -- OBJ_HIP_BOOTS
|
|
[13] = 3, -- OBJ_THIGH_BOOTS
|
|
[17] = 2, -- OBJ_CLOTH_JACKET
|
|
[18] = 3, -- OBJ_WOOL_SWEATER
|
|
[19] = 3, -- OBJ_CAPE
|
|
[20] = 4, -- OBJ_DUSTER
|
|
[21] = 4, -- OBJ_WOOL_OVERCOAT
|
|
[22] = 3, -- OBJ_SHEEPSKIN_JACKET
|
|
[23] = 5, -- OBJ_ARCTIC_PARKA
|
|
[90] = 12, -- OBJ_ELECTRIC_BELT
|
|
[25] = 2, -- OBJ_COTTON_PANTS
|
|
[26] = 2, -- OBJ_COTTON_DRESS
|
|
[27] = 2, -- OBJ_DENIM_JEANS
|
|
[28] = 3, -- OBJ_WOOL_PANTS
|
|
[29] = 3, -- OBJ_WOOL_DRESS
|
|
[30] = 3, -- OBJ_CHAPS_AND_JEANS
|
|
[33] = 1, -- OBJ_LADYS_SILK_GLOVES
|
|
[34] = 1, -- OBJ_DRIVING_GLOVES
|
|
[35] = 1, -- OBJ_COTTON_WORK_GLOVES
|
|
[36] = 2, -- OBJ_WORK_GLOVES
|
|
[37] = 3, -- OBJ_WOOL_MITTENS
|
|
[38] = 1, -- OBJ_RUBBER_GLOVES
|
|
[39] = 3, -- OBJ_WELDING_GLOVES
|
|
}
|
|
|
|
function clothing_get_warmth_rating(obj)
|
|
local rating = clothing_warmth_tbl[obj.obj_n]
|
|
if rating == nil then
|
|
rating = 0
|
|
end
|
|
|
|
return rating
|
|
end
|
|
|
|
local lit_lightsource_tbl = {
|
|
[110] = 1, -- OBJ_LIT_TORCH
|
|
[112] = 1, -- OBJ_LIT_CANDLE
|
|
[114] = 1, -- OBJ_LIT_CANDELABRA
|
|
[116] = 1, -- OBJ_LIT_OIL_LAMP
|
|
[118] = 1, -- OBJ_LIT_LANTERN
|
|
}
|
|
|
|
function is_lit_lightsource(obj)
|
|
if lit_lightsource_tbl[obj.obj_n] ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function actor_str_adj(actor)
|
|
local actor_num = actor.actor_num
|
|
local str = actor.str
|
|
|
|
if actor.hypoxia then
|
|
str = str - 3
|
|
end
|
|
|
|
if actor_is_affected_by_purple_berries(actor_num) then
|
|
str = str - 3
|
|
end
|
|
|
|
if actor.frenzy then
|
|
str = str + 3
|
|
if str > 30 then
|
|
str = 30
|
|
end
|
|
end
|
|
|
|
if str <= 3 then
|
|
return 1
|
|
end
|
|
|
|
return str
|
|
end
|
|
|
|
function actor_dex_adj(actor)
|
|
|
|
local dex = actor.dex
|
|
if actor.hypoxia then
|
|
if dex <= 3 then
|
|
dex = 1
|
|
else
|
|
dex = dex - 3
|
|
end
|
|
end
|
|
|
|
if actor.frenzy then
|
|
dex = dex + 3
|
|
if dex >= 30 then
|
|
dex = 30
|
|
end
|
|
end
|
|
|
|
if actor.asleep then
|
|
dex = 1
|
|
end
|
|
|
|
return dex
|
|
end
|
|
|
|
function actor_int_adj(actor)
|
|
local int = actor.int
|
|
|
|
if actor.hypoxia == true or (actor.frenzy and actor.actor_num ~= 1) then
|
|
int = int - 3
|
|
end
|
|
|
|
if int < 1 then int = 1 end
|
|
|
|
return int
|
|
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]
|
|
local player_loc = player_get_location()
|
|
|
|
|
|
if is_actor_stat_bit_set(obj_n, 8) or is_actor_stat_bit_set(obj_n, 10)
|
|
or actor.z ~= player_loc.z
|
|
or actor_find_max_wrapped_xy_distance(actor, player_loc.x, player_loc.y) > 40 then
|
|
return
|
|
end
|
|
|
|
if actor.alive == false or actor.hit_flag == true then
|
|
return
|
|
end
|
|
--print("actor_map_dmg("..actor.actor_num..")\n")
|
|
|
|
for obj in objs_at_loc(actor.x, actor.y, actor.z) do
|
|
local tile_num = obj.tile_num_original
|
|
if tile_get_flag(tile_num, 3, 2) == false and tile_get_flag(tile_num, 1, 3) == true then --force passable and damaging
|
|
actor_take_damage_from_obj(actor, obj)
|
|
end
|
|
|
|
end
|
|
|
|
actor_update_frame(actor, actor.direction)
|
|
end
|
|
|
|
function actor_take_damage_from_obj(actor, obj)
|
|
if actor_immune_to_dmg(actor) or actor.alive == false then
|
|
return
|
|
end
|
|
|
|
local obj_n = obj.obj_n
|
|
local damage = 0
|
|
local damage_mode = 0
|
|
|
|
if obj_n == 168 then --OBJ_PORCUPOD
|
|
damage = math.random(1, 0x14)
|
|
elseif obj_n == 209 then --OBJ_STEAM_LEAK
|
|
damage_mode = 3
|
|
if actor.actor_num ~= 6 then
|
|
damage = math.random(math.floor(actor.hp / 2), math.floor(actor.max_hp / 2))
|
|
end
|
|
elseif obj_n == 215 then --OBJ_POWER_CABLE1
|
|
play_md_sfx(0x15)
|
|
damage = math.random(1, 0x14) + math.random(1, 0x14)
|
|
elseif obj_n == 381 then --OBJ_DUST_DEVIL
|
|
damage = math.random(1, 0xa) + math.random(1, 0xa)
|
|
elseif obj_n == 463 then --hidden effect obj
|
|
--FIXME here
|
|
end
|
|
|
|
actor_hit(actor, damage, damage_mode)
|
|
end
|
|
|
|
function actor_remove_charm(actor)
|
|
|
|
actor.charmed = false;
|
|
actor.align = actor.old_align
|
|
|
|
if actor.in_party then
|
|
actor.align = ALIGNMENT_GOOD
|
|
end
|
|
|
|
if party_is_in_combat_mode() then
|
|
actor.wt = actor.combat_mode
|
|
else
|
|
if player_is_in_solo_mode() then
|
|
actor.wt = WT_NOTHING
|
|
else
|
|
actor.wt = WT_FOLLOW
|
|
end
|
|
end
|
|
|
|
party_update_leader()
|
|
|
|
end
|
|
|
|
function advance_time(num_turns)
|
|
--FIXME
|
|
local rand = math.random
|
|
local hour = clock_get_hour()
|
|
|
|
local quake = Actor.get_talk_flag(0x46, 3) --rasputin
|
|
|
|
if quake then
|
|
if rand(0, 4) == 0 then
|
|
quake_start(1, 200)
|
|
end
|
|
end
|
|
|
|
local max_light = 0
|
|
|
|
local actor_num
|
|
for actor_num=0,0xff do
|
|
local actor = Actor.get(actor_num)
|
|
if actor.alive then
|
|
if g_in_dream_mode == false and actor.in_party and actor_num ~= 0 then
|
|
if num_turns > 0 and actor.asleep == false then --FIXME I think we also need to check distance < 0x27 from either mapwindow or player.
|
|
if actor_num == 6 or Actor.get_talk_flag(0x10, 5) == false or Actor.inv_has_obj_n(actor, 131) then --OBJ_BLOB_OF_OXIUM
|
|
if actor.hypoxia then
|
|
actor.hypoxia = false
|
|
printfl("BREATHES_EASIER", actor.name)
|
|
end
|
|
else
|
|
if actor.hypoxia == false then
|
|
actor.hypoxia = true
|
|
printfl("GASPS_FOR_AIR", actor.name)
|
|
end
|
|
end
|
|
|
|
local warmth_rating = 0
|
|
local obj
|
|
for obj in actor_inventory(actor) do
|
|
if obj.readied then
|
|
warmth_rating = warmth_rating + clothing_get_warmth_rating(obj)
|
|
|
|
if is_lit_lightsource(obj) then
|
|
if rand(0, 1) == 1 then
|
|
if obj.quality <= num_turns then
|
|
if obj.obj_num == 116 --OBJ_LIT_OIL_LAMP
|
|
or obj.obj_num == 118 then --OBJ_LIT_LANTERN
|
|
if obj.obj_num == 116 then --OBJ_LIT_OIL_LAMP
|
|
obj.obj_n = 115 --OBJ_OIL_LAMP
|
|
else --OBJ_LIT_LANTERN
|
|
obj.obj_n = 117--OBJ_LANTERN
|
|
end
|
|
obj.quality = 0
|
|
printfl("THE_IS_OUT_OF_FUEL", obj.name)
|
|
else
|
|
printfl("WENT_OUT", obj.look_string)
|
|
Obj.removeFromEngine(obj)
|
|
end
|
|
else
|
|
obj.quality = obj.quality - num_turns
|
|
end
|
|
end
|
|
--FIXME update max_light here. probably not needed as light is updated elsewhere in nuvie.
|
|
--if max_light < obj.light then
|
|
--max_light = obj.light
|
|
--end
|
|
end
|
|
end
|
|
end
|
|
|
|
if g_party_is_warm or actor.z ~= 0 then
|
|
if actor.cold then
|
|
actor.cold = false
|
|
printfl("FEELS_WARMER", actor.name)
|
|
end
|
|
else
|
|
local cold_status = 0
|
|
|
|
if hour <= 3 or hour >= 22 then
|
|
if warmth_rating >= 10 then
|
|
cold_status = 1
|
|
else
|
|
cold_status = 2
|
|
end
|
|
elseif hour <= 6 or hour >= 18 then
|
|
if warmth_rating < 10 then
|
|
cold_status = 1
|
|
end
|
|
end
|
|
|
|
if actor_num == 6 then
|
|
cold_status = 0
|
|
end
|
|
|
|
if cold_status == 0 then
|
|
if actor.cold then
|
|
actor.cold = false
|
|
printfl("FEELS_WARMER", actor.name)
|
|
end
|
|
else
|
|
if actor.cold == false then
|
|
actor.cold = true
|
|
printfl("IS_FREEZING", actor.name)
|
|
end
|
|
if num_turns ~= 0 then
|
|
for i=1,num_turns do
|
|
if rand(0, 1) == 0 then
|
|
if cold_status == 2 then
|
|
printfl("IS_FREEZING", actor.name)
|
|
actor_hit(actor, rand(1, 2))
|
|
end
|
|
else
|
|
printfl("IS_FREEZING", actor.name)
|
|
actor_hit(actor, rand(1, 2))
|
|
if cold_status == 2 then
|
|
actor_hit(actor, rand(1, 2))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
if num_turns ~= 0 then
|
|
for i=1,num_turns do
|
|
--FIXME what does word_4E6FA do?
|
|
|
|
--remove battle frenzy from actor when the party exits combat mode
|
|
if actor.frenzy and not party_is_in_combat_mode() then
|
|
actor.frenzy = false
|
|
end
|
|
|
|
if actor.poisoned then
|
|
if rand(0, 25) == 0 then
|
|
actor.poisoned = false
|
|
end
|
|
end
|
|
|
|
if actor.charmed and rand(0, 0x19) == 0 then
|
|
actor_remove_charm(actor)
|
|
end
|
|
|
|
if actor.paralyzed then
|
|
if actor_num == 6 or (rand(0, 3) == 0 and actor.str >= rand(1, 0x1e)) then --FIXME used adjusted str
|
|
actor.paralyzed = false
|
|
end
|
|
end
|
|
|
|
if actor.asleep and actor.wt ~= WT_SLEEP and (not g_party_is_warm or not actor.in_party) then
|
|
--FIXME check sub_2B0EC(actor.x,actor.y,actor.z)
|
|
if rand(0,0x14) == 0 then
|
|
actor.asleep = false
|
|
--FIXME bit 3 set on 1af1 flags
|
|
end
|
|
end
|
|
|
|
if actor.poisoned and actor_num ~= 6 and rand(0, 7) == 0 then
|
|
actor_hit(actor, 1)
|
|
end
|
|
|
|
if actor_num < 8 then
|
|
actor_decrement_berry_counters(actor)
|
|
|
|
for obj in actor_inventory(actor, true) do
|
|
local obj_n = obj.obj_n
|
|
if obj_n == 160 and obj.frame_n > 1 then --OBJ_EMPTY_BUCKET with ice
|
|
if rand(0, 100) < 10 then
|
|
printl("SOME_ICE_HAS_MELTED")
|
|
obj.frame_n = 1
|
|
end
|
|
elseif obj_n == 256 then --OBJ_CHUNK_OF_ICE
|
|
if rand(0, 100) < 10 then
|
|
printl("SOME_ICE_HAS_MELTED")
|
|
Obj.removeFromEngine(obj)
|
|
end
|
|
elseif obj_n == 448 or (obj_n == 449 and actor_num ~= 6 and actor.poisoned == false) then --OBJ_BLOCK_OF_RADIUM, OBJ_CHIP_OF_RADIUM
|
|
if obj.in_container == false or obj.parent.obj_n ~= 139 then --OBJ_LEAD_BOX
|
|
printfl("IS_POISONED", actor.name)
|
|
actor.poisoned = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
actor_map_dmg(actor, actor.x, actor.y, actor.z)
|
|
actor.hit_flag = false
|
|
end
|
|
end
|
|
|
|
local minute = clock_get_minute()
|
|
|
|
clock_inc(num_turns)
|
|
|
|
if minute + num_turns >= 60 then
|
|
|
|
update_watch_tile()
|
|
|
|
update_actor_schedules()
|
|
if g_hours_till_next_healing > 0 then
|
|
g_hours_till_next_healing = g_hours_till_next_healing - 1
|
|
end
|
|
|
|
update_lamp_posts()
|
|
|
|
local blue_berry_counter = actor_get_blue_berry_counter()
|
|
if blue_berry_counter > 0 then
|
|
actor_get_blue_berry_counter(blue_berry_counter - 1)
|
|
end
|
|
|
|
if not g_party_is_warm and not g_in_dream_mode and Actor.get_talk_flag(0x10, 5) then
|
|
for actor in party_members() do
|
|
if actor.actor_num ~= 6 and not actor.asleep then
|
|
local oxium = Actor.inv_get_obj_n(actor, 131) --OBJ_BLOB_OF_OXIUM
|
|
if oxium ~= nil then
|
|
if oxium.qty > 1 then
|
|
oxium.qty = oxium.qty - 1
|
|
if actor.hypoxia then
|
|
actor.hypoxia = false
|
|
printfl("BREATHES_EASIER", actor.name)
|
|
end
|
|
else
|
|
Obj.removeFromEngine(oxium)
|
|
if Actor.inv_get_obj_n(actor, 131) ~= nil then
|
|
if actor.hypoxia then
|
|
actor.hypoxia = false
|
|
printfl("BREATHES_EASIER", actor.name)
|
|
end
|
|
else
|
|
if actor.hypoxia == false then
|
|
actor.hypoxia = true
|
|
printfl("GASPS_FOR_AIR", actor.name)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if actor.hypoxia == false then
|
|
actor.hypoxia = true
|
|
printfl("GASPS_FOR_AIR", actor.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
function can_get_obj_override(obj)
|
|
return false
|
|
end
|
|
|
|
function subtract_movement_pts(actor, points)
|
|
if actor.actor_num < 16 then
|
|
if party_is_in_combat_mode() == false then
|
|
points = points - 2
|
|
end
|
|
end
|
|
|
|
if points < 1 then
|
|
points = 1
|
|
end
|
|
|
|
actor.mpts = actor.mpts - points
|
|
end
|
|
|
|
function actor_radiation_check(actor, obj)
|
|
if obj.obj_n == 448 or obj.obj_n == 449 then --OBJ_BLOCK_OF_RADIUM, OBJ_CHIP_OF_RADIUM
|
|
|
|
local actor_num = actor.actor_num
|
|
if actor_num == 6
|
|
or actor_is_affected_by_purple_berries(actor_num) -- purple berries protect against radiation
|
|
or Actor.inv_get_readied_obj_n(actor, ARM) == 136 --OBJ_TONGS
|
|
or Actor.inv_get_readied_obj_n(actor, ARM_2) == 136 then
|
|
return
|
|
end
|
|
|
|
actor.poisoned = true
|
|
printl("OUCH_IT_IS_VERY_HOT")
|
|
end
|
|
end
|
|
|
|
function actor_get_obj(actor, obj, container) -- FIXME need to limit inventory slots
|
|
|
|
--FIXME handle getting into container.
|
|
|
|
if obj.getable == false then
|
|
printnl("THAT_IS_NOT_POSSIBLE")
|
|
return false
|
|
end
|
|
|
|
if actor_find_max_wrapped_xy_distance(actor, obj.x, obj.y) > 1 then
|
|
play_md_sfx(0x32)
|
|
projectile_anim(obj.tile_num, obj.x, obj.y, actor.x, actor.y, 4, false, 0)
|
|
end
|
|
|
|
if Actor.can_carry_obj_weight(actor, obj) == false then
|
|
printl("THE_TOTAL_IS_TOO_HEAVY")
|
|
return false
|
|
end
|
|
|
|
if not Actor.can_carry_obj(actor, obj) then
|
|
printnl("YOU_ARE_CARRYING_TOO_MUCH_ALREADY")
|
|
return false
|
|
end
|
|
|
|
subtract_movement_pts(actor, 3)
|
|
|
|
actor_radiation_check(actor, obj)
|
|
|
|
if obj.obj_n == 256 then -- OBJ_CHUNK_OF_ICE
|
|
printnl("THE_ICE_IS_MELTING")
|
|
end
|
|
|
|
if obj.obj_n == 110 -- OBJ_LIT_TORCH
|
|
or obj.obj_n == 112
|
|
or obj.obj_n == 114
|
|
or obj.obj_n == 116
|
|
or obj.obj_n == 118 then
|
|
if not actor_has_free_arm(actor) then
|
|
printl("YOUR_HANDS_ARE_FULL")
|
|
return false
|
|
end
|
|
|
|
Obj.moveToInv(obj, actor.actor_num)
|
|
Actor.inv_ready_obj(actor, obj)
|
|
if obj.obj_n == 110 then -- OBJ_LIT_TORCH
|
|
obj.quality = 0xb4
|
|
end
|
|
|
|
advance_time(0)
|
|
|
|
return true
|
|
end
|
|
|
|
if obj.obj_n == 411 -- OBJ_SWITCH_BAR
|
|
or obj.obj_n == 458 -- OBJ_PANEL
|
|
or obj.obj_n == 314 -- OBJ_TRACKING_MOTOR
|
|
or obj.obj_n == 206 then -- OBJ_TIFFANY_LENS
|
|
if obj.quality % 2 == 0 then
|
|
printnl("WONT_BUDGE")
|
|
return false
|
|
end
|
|
end
|
|
|
|
Obj.moveToInv(obj, actor.actor_num)
|
|
|
|
return true
|
|
end
|
|
|
|
function actor_get_max_hp(actor)
|
|
if actor.actor_num == 6 then
|
|
return 0xf0
|
|
end
|
|
|
|
if actor.in_party then
|
|
return actor.str * 2 + actor.level * 24
|
|
end
|
|
|
|
--FIXME return actor max hp from stat table.
|
|
return 1;
|
|
end
|
|
|
|
|
|
|
|
--function actor_take_hit(actor, damage)
|
|
-- local hp = actor.hp
|
|
-- if damage >= hp and actor.actor_num == 1 then
|
|
-- hit_anim(actor.x, actor.y)
|
|
-- actor.hp = 0
|
|
-- else
|
|
-- Actor.hit(actor, damage)
|
|
-- end
|
|
--end
|
|
|
|
function kill_actor(actor)
|
|
actor.hp=0
|
|
actor_dead(actor)
|
|
end
|
|
|
|
function actor_dead(actor)
|
|
--FIXME
|
|
end
|
|
|
|
function get_portrait_number(actor)
|
|
local NO_PORTRAIT = 255
|
|
local idx = actor.actor_num
|
|
if idx >= 0xa and idx <= 0xf then
|
|
idx = 0x51
|
|
end
|
|
|
|
if idx >= 0xbf then
|
|
return NO_PORTRAIT
|
|
end
|
|
|
|
if idx <= 1 then
|
|
if player_get_gender() == 1 then
|
|
idx = 0
|
|
else
|
|
idx = 1
|
|
end
|
|
elseif idx == 6 and Actor.get_talk_flag(actor, 1) then
|
|
idx = 0x16
|
|
elseif actor.obj_flag_0 then
|
|
if player_get_gender() == 1 then
|
|
idx = 0x73
|
|
else
|
|
idx = 0x72
|
|
end
|
|
elseif idx == 0x21 and not Actor.get_talk_flag(actor, 2) then
|
|
idx = 0x61
|
|
elseif idx == 0x15 or (idx >= 0x47 and idx <= 0x4a) or idx == 0x4e then
|
|
idx = 0x14
|
|
elseif idx >= 0x4b and idx <= 0x4d then
|
|
idx = 0x16
|
|
elseif idx == 0x1e then
|
|
idx = 0x18
|
|
elseif idx == 0x46 then
|
|
idx = 0x40
|
|
elseif idx >= 0x50 and idx <= 0x57 then
|
|
idx = idx - 0x26
|
|
elseif idx == 0x5e then
|
|
idx = 0x6
|
|
elseif idx == 0x68 or idx == 0x77 or idx == 0x78 then
|
|
idx = 0x67
|
|
elseif idx == 0x69 then
|
|
idx = 0x19
|
|
elseif idx == 0x6d then
|
|
idx = 0x2
|
|
end
|
|
|
|
return idx
|
|
end
|
|
|
|
function find_rockworm_actor(obj)
|
|
if obj ~= nil then
|
|
if obj.obj_n == 371 or obj.obj_n == 370 then --OBJ_ROCKWORM1, OBJ_ROCKWORM_BASE
|
|
local actor = Actor.get(obj.quality)
|
|
if actor.alive then
|
|
return actor
|
|
end
|
|
end
|
|
end
|
|
|
|
return obj
|
|
end
|