575 lines
13 KiB
Lua
575 lines
13 KiB
Lua
local lua_file = nil
|
|
|
|
--load common functions
|
|
lua_file = nuvie_load("common/common.lua"); lua_file();
|
|
|
|
|
|
SFX_BLOCKED = 0
|
|
SFX_HIT = 1
|
|
SFX_FOUNTAIN = 2
|
|
SFX_DEATH = 3
|
|
SFX_RUBBER_DUCK = 4
|
|
SFX_BROKEN_GLASS = 5
|
|
SFX_BELL = 6
|
|
SFX_FIRE = 7
|
|
SFX_CLOCK = 8
|
|
SFX_PROTECTION_FIELD = 9
|
|
SFX_WATER_WHEEL = 10
|
|
SFX_MISSLE = 11
|
|
SFX_EXPLOSION = 12
|
|
SFX_ATTACK_SWING = 13
|
|
SFX_SUCCESS = 14
|
|
SFX_FAILURE = 15
|
|
SFX_CORPSER_DRAGGED_UNDER = 16
|
|
SFX_CORPSER_REGURGITATE = 17
|
|
SFX_CASTING_MAGIC_P1 = 18
|
|
SFX_CASTING_MAGIC_P1_2 = 19
|
|
SFX_CASTING_MAGIC_P1_3 = 20
|
|
SFX_CASTING_MAGIC_P1_4 = 21
|
|
SFX_CASTING_MAGIC_P1_5 = 22
|
|
SFX_CASTING_MAGIC_P1_6 = 23
|
|
SFX_CASTING_MAGIC_P1_7 = 24
|
|
SFX_CASTING_MAGIC_P1_8 = 25
|
|
SFX_CASTING_MAGIC_P2 = 26
|
|
SFX_CASTING_MAGIC_P2_2 = 27
|
|
SFX_CASTING_MAGIC_P2_3 = 28
|
|
SFX_CASTING_MAGIC_P2_4 = 29
|
|
SFX_CASTING_MAGIC_P2_5 = 30
|
|
SFX_CASTING_MAGIC_P2_6 = 31
|
|
SFX_CASTING_MAGIC_P2_7 = 32
|
|
SFX_CASTING_MAGIC_P2_8 = 33
|
|
SFX_AVATAR_DEATH = 34
|
|
SFX_KAL_LOR = 35
|
|
SFX_SLUG_DISSOLVE = 36
|
|
SFX_HAIL_STONE = 37
|
|
|
|
SFX_EARTH_QUAKE = 39
|
|
|
|
FADE_COLOR_RED = 12
|
|
FADE_COLOR_BLUE = 9
|
|
|
|
TIMER_LIGHT = 0
|
|
TIMER_INFRAVISION = 1
|
|
TIMER_STORM = 13
|
|
TIMER_TIME_STOP = 14
|
|
TIMER_ECLIPSE = 15
|
|
|
|
OBJLIST_OFFSET_VANISH_OBJ = 0x1c13
|
|
OBJLIST_OFFSET_MOONSTONES = 0x1c1b
|
|
OBJLIST_OFFSET_KEG_TIMER = 0x1c4b
|
|
|
|
g_vanish_obj = {["obj_n"] = 0, ["frame_n"] = 0}
|
|
|
|
g_keg_timer = 0
|
|
g_armageddon = false
|
|
g_avatar_died = false -- used so we don't keep casting once Avatar is dead
|
|
function is_avatar_dead()
|
|
return g_avatar_died
|
|
end
|
|
|
|
--used with triple crossbow and magic wind spells.
|
|
g_projectile_offset_tbl =
|
|
{
|
|
{
|
|
4,5,5,5,5,6,6,6,6,6,6,
|
|
4,4,5,5,5,6,6,6,6,6,7,
|
|
4,4,4,5,5,6,6,6,6,7,7,
|
|
4,4,4,4,5,6,6,6,7,7,7,
|
|
4,4,4,4,4,6,6,7,7,7,7,
|
|
4,4,4,4,4,0,0,0,0,0,0,
|
|
3,3,3,3,2,2,0,0,0,0,0,
|
|
3,3,3,2,2,2,1,0,0,0,0,
|
|
3,3,2,2,2,2,1,1,0,0,0,
|
|
3,2,2,2,2,2,1,1,1,0,0,
|
|
2,2,2,2,2,2,1,1,1,1,0
|
|
},
|
|
{
|
|
2,2,2,2,2,2,3,3,3,3,4,
|
|
1,2,2,2,2,2,3,3,3,4,4,
|
|
1,1,2,2,2,2,3,3,4,4,4,
|
|
1,1,1,2,2,2,3,4,4,4,4,
|
|
1,1,1,1,2,2,4,4,4,4,4,
|
|
0,0,0,0,0,0,4,4,4,4,4,
|
|
0,0,0,0,0,6,6,5,5,5,5,
|
|
0,0,0,0,7,6,6,6,5,5,5,
|
|
0,0,0,7,7,6,6,6,6,5,5,
|
|
0,0,7,7,7,6,6,6,6,6,5,
|
|
0,7,7,7,7,6,6,6,6,6,6
|
|
}
|
|
}
|
|
|
|
--moonstone data is loaded from objlist in load_game()
|
|
g_moonstone_loc_tbl =
|
|
{
|
|
{x=0x3A7, y=0x106, z=0},
|
|
{x=0x1F7, y=0x166, z=0},
|
|
{x=0x9F, y=0x3AE, z=0},
|
|
{x=0x127, y=0x26, z=0},
|
|
{x=0x33F, y=0x0A6, z=0},
|
|
{x=0x147, y=0x336, z=0},
|
|
{x=0x17, y=0x16, z=1},
|
|
{x=0x397, y=0x3A6, z=0}
|
|
}
|
|
|
|
g_show_stealing = config_get_boolean_value("config/ultima6/show_stealing")
|
|
|
|
-- some common functions
|
|
|
|
function set_g_show_stealing(stealing)
|
|
g_show_stealing = stealing
|
|
end
|
|
|
|
function dbg(msg_string)
|
|
--io.stderr:write(msg_string)
|
|
end
|
|
|
|
function alignment_is_evil(align)
|
|
if align == ALIGNMENT_EVIL or align == ALIGNMENT_CHAOTIC then return true end
|
|
|
|
return false
|
|
end
|
|
|
|
function advance_game_time(nturns)
|
|
if nturns == 0 then return end
|
|
|
|
coroutine.yield("adv_game_time", nturns);
|
|
end
|
|
|
|
function get_obj_from_inventory(actor)
|
|
local obj = coroutine.yield("inv_obj", actor)
|
|
return obj
|
|
end
|
|
|
|
function get_obj()
|
|
local obj = coroutine.yield("obj")
|
|
return obj
|
|
end
|
|
|
|
function actor_talk(actor)
|
|
coroutine.yield("talk", actor)
|
|
return
|
|
end
|
|
|
|
function get_spell()
|
|
local spell_num = coroutine.yield("spell")
|
|
return spell_num
|
|
end
|
|
|
|
function obj_new(obj_n, frame_n, status, qty, quality, x, y, z)
|
|
local obj = {}
|
|
|
|
obj["obj_n"] = obj_n or 0
|
|
obj["frame_n"] = frame_n or 0
|
|
obj["status"] = status or 0
|
|
obj["qty"] = qty or 0
|
|
obj["quality"] = quality or 0
|
|
|
|
obj["x"] = x or 0
|
|
obj["y"] = y or 0
|
|
obj["z"] = z or 0
|
|
|
|
return obj
|
|
end
|
|
|
|
--FIXME need a better way of doing this. Remove need for deprecated setfenv() function.
|
|
function run_script(script)
|
|
local t = {};
|
|
setmetatable(t, {__index = _G});
|
|
local body = nuvie_load(script);
|
|
setfenv(body, t);
|
|
body();
|
|
end
|
|
|
|
function look_obj(obj)
|
|
print("Thou dost see " .. obj.look_string);
|
|
local weight = obj.weight; --FIXME this could be a problem if we want to change Lua_number type to int.
|
|
if weight ~= 0 then
|
|
if obj.qty > 1 and obj.stackable then
|
|
print(". They weigh");
|
|
else
|
|
print(". It weighs");
|
|
end
|
|
|
|
print(string.format(" %.1f", weight).." stones");
|
|
end
|
|
|
|
--FIXME usecode look description should be lua code.
|
|
if usecode_look(obj) then
|
|
print("\n")
|
|
return false
|
|
end
|
|
|
|
local dmg = weapon_dmg_tbl[obj.obj_n];
|
|
if dmg ~= nil then
|
|
if weight ~= 0 then
|
|
print(" and")
|
|
else
|
|
print(". It")
|
|
end
|
|
|
|
print(" can do "..dmg.." point")
|
|
if dmg > 1 then print("s") end
|
|
print(" of damage")
|
|
|
|
end
|
|
|
|
local ac = armour_tbl[obj.obj_n]
|
|
if ac ~= nil then
|
|
if weight ~= 0 or dmg ~= 0 then
|
|
print(" and")
|
|
else
|
|
print(". It")
|
|
end
|
|
|
|
print(" can absorb "..ac.." point")
|
|
if ac > 1 then print("s") end
|
|
print(" of damage")
|
|
end
|
|
|
|
print(".\n");
|
|
local player_loc = player_get_location();
|
|
if g_show_stealing == true and obj.getable == true and player_loc.z == 0 and obj.ok_to_take == false then
|
|
if math.abs(player_loc.x - obj.x) > 1 or math.abs(player_loc.y - obj.y) > 1 then
|
|
print("PRIVATE PROPERTY\n")
|
|
else
|
|
print("PRIVATE PROPERTY")
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function player_subtract_karma(k)
|
|
local karma = player_get_karma() - k
|
|
if karma < 0 then karma = 0 end
|
|
|
|
player_set_karma(karma)
|
|
end
|
|
|
|
function player_add_karma(k)
|
|
local karma = player_get_karma() + k
|
|
if karma >= 100 then karma = 99 end
|
|
|
|
player_set_karma(karma)
|
|
end
|
|
|
|
function party_heal()
|
|
for actor in party_members() do
|
|
actor.asleep = false
|
|
actor.poisoned = false
|
|
actor.paralyzed = false
|
|
actor_remove_charm(actor)
|
|
actor.hp = actor.max_hp
|
|
end
|
|
end
|
|
|
|
function explosion(tile_num, x, y)
|
|
play_sfx(SFX_EXPLOSION)
|
|
return explosion_start(tile_num, x, y)
|
|
end
|
|
|
|
function projectile(tile_num, start_x, start_y, end_x, end_y, speed, spin)
|
|
|
|
if spin == nil then spin = 0 end
|
|
|
|
local rotate_offset = 0
|
|
local src_tile_y_offset = 0
|
|
|
|
if tile_num == 547 then --spear
|
|
rotate_offset = 45
|
|
elseif tile_num == 566 then --bow
|
|
rotate_offset = 90
|
|
src_tile_y_offset = 4
|
|
elseif tile_num == 567 then --crossbow
|
|
rotate_offset = 90
|
|
src_tile_y_offset = 3
|
|
end
|
|
|
|
play_sfx(SFX_MISSLE)
|
|
projectile_anim(tile_num, start_x, start_y, end_x, end_y, speed, false, rotate_offset, spin, src_tile_y_offset)
|
|
end
|
|
|
|
function fade_obj_blue(obj)
|
|
fade_obj(obj, FADE_COLOR_BLUE, 20)
|
|
end
|
|
|
|
function fade_actor_blue(actor)
|
|
Actor.black_fade_effect(actor, FADE_COLOR_BLUE, 20)
|
|
end
|
|
|
|
function fade_obj_out(obj)
|
|
obj.invisible = true
|
|
fade_tile(obj.x, obj.y, obj.z, obj.tile_num)
|
|
obj.invisible = false
|
|
end
|
|
|
|
function fade_obj_in(obj)
|
|
obj.invisible = true
|
|
fade_tile(obj.x, obj.y, obj.z, nil, obj.tile_num)
|
|
obj.invisible = false
|
|
end
|
|
|
|
function fade_actor_in(actor)
|
|
local new_tile_num = actor.tile_num
|
|
actor.visible = false
|
|
fade_tile(actor.x, actor.y, actor.z, nil, new_tile_num)
|
|
actor.visible = true
|
|
end
|
|
|
|
--tile_num, readied location
|
|
local g_readiable_objs_tbl = {
|
|
[0x200] = 0, [0x201] = 0, [0x202] = 0, [0x203] = 0, [0x204] = 0, [0x205] = 0, [0x206] = 0, [0x207] = 0,
|
|
|
|
[0x219] = 1, [0x250] = 1, [0x251] = 1, [0x252] = 1, [0x217] = 1, [0x101] = 1,
|
|
|
|
[0x220] = 2, [0x221] = 2, [0x223] = 2, [0x224] = 2, [0x225] = 2, [0x226] = 2, [0x227] = 2, [0x22A] = 2,
|
|
[0x22F] = 2, [0x230] = 2, [0x238] = 2, [0x254] = 2, [0x256] = 2, [0x255] = 2, [0x259] = 2, [0x262] = 2,
|
|
[0x263] = 2, [0x264] = 2, [0x270] = 2, [0x271] = 2, [0x272] = 2, [0x273] = 2, [0x274] = 2, [0x275] = 2,
|
|
[0x279] = 2, [0x27D] = 2, [0x27E] = 2, [0x27F] = 2, [0x280] = 2, [0x281] = 2, [0x28C] = 2, [0x28E] = 2,
|
|
[0x29D] = 2, [0x2A2] = 2, [0x2A3] = 2, [0x2B9] = 2,
|
|
|
|
[0x210] = 4, [0x211] = 4, [0x212] = 4, [0x213] = 4, [0x214] = 4, [0x215] = 4, [0x216] = 4, [0x218] = 4,
|
|
[0x219] = 4, [0x28c] = 4, [0x28e] = 4, [0x29d] = 4, [0x257] = 4,
|
|
[0x208] = 5, [0x209] = 5, [0x20a] = 5, [0x20b] = 5, [0x20c] = 5, [0x20d] = 5, [0x20e] = 5, [0x20f] = 5,
|
|
[0x222] = 5,
|
|
|
|
[0x21a] = 7, [0x21b] = 7,
|
|
|
|
[0x228] = 8, [0x229] = 8, [0x231] = 8, [0x235] = 8, [0x22b] = 8, [0x22c] = 8, [0x22d] = 8, [0x22e] = 8,
|
|
|
|
[0x258] = 9, [0x37d] = 9, [0x37e] = 9, [0x37f] = 9
|
|
}
|
|
|
|
function actor_is_readiable_obj(actor)
|
|
if g_readiable_objs_tbl[actor.tile_num] ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function obj_is_readiable(obj)
|
|
if g_readiable_objs_tbl[obj.tile_num] ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function is_time_stopped()
|
|
if timer_get(TIMER_TIME_STOP) ~= 0 then return true end
|
|
|
|
return false
|
|
end
|
|
|
|
function load_game()
|
|
objlist_seek(OBJLIST_OFFSET_VANISH_OBJ)
|
|
local tmp_obj_dat = objlist_read2()
|
|
|
|
local obj_n = tmp_obj_dat
|
|
local frame_n = 0
|
|
if tmp_obj_dat > 1023 then
|
|
frame_n = tmp_obj_dat - 1023
|
|
obj_n = tmp_obj_dat - frame_n
|
|
end
|
|
|
|
g_vanish_obj.obj_n = obj_n
|
|
g_vanish_obj.frame_n = frame_n
|
|
|
|
--Load moonstone locations.
|
|
objlist_seek(OBJLIST_OFFSET_MOONSTONES)
|
|
|
|
for i=1,8 do
|
|
|
|
local x = objlist_read2()
|
|
local y = objlist_read2()
|
|
local z = objlist_read2()
|
|
dbg("moonstone["..i.."] at ("..x..","..y..","..z..")\n")
|
|
g_moonstone_loc_tbl[i] = {x=x, y=y, z=z}
|
|
end
|
|
|
|
objlist_seek(OBJLIST_OFFSET_KEG_TIMER)
|
|
g_keg_timer = objlist_read2()
|
|
set_g_armageddon(false)
|
|
g_avatar_died = false
|
|
end
|
|
|
|
function save_game()
|
|
objlist_seek(OBJLIST_OFFSET_VANISH_OBJ)
|
|
|
|
local tmp_obj_dat = g_vanish_obj.obj_n
|
|
local frame_n = g_vanish_obj.frame_n
|
|
|
|
if frame_n > 0 then
|
|
tmp_obj_dat = tmp_obj_dat + (frame_n + 1023)
|
|
end
|
|
|
|
objlist_write2(tmp_obj_dat)
|
|
|
|
--Save moonstone locations
|
|
objlist_seek(OBJLIST_OFFSET_MOONSTONES)
|
|
|
|
for i=1,8 do
|
|
local loc = g_moonstone_loc_tbl[i]
|
|
objlist_write2(loc.x)
|
|
objlist_write2(loc.y)
|
|
objlist_write2(loc.z)
|
|
end
|
|
|
|
objlist_seek(OBJLIST_OFFSET_KEG_TIMER)
|
|
objlist_write2(g_keg_timer)
|
|
end
|
|
|
|
function moonstone_set_loc(phase, x, y, z)
|
|
if phase < 1 or phase > 8 then return false end
|
|
|
|
g_moonstone_loc_tbl[phase] = {x=x, y=y, z=z}
|
|
return true
|
|
end
|
|
|
|
function moonstone_get_loc(phase)
|
|
if phase < 1 or phase > 8 then return nil end
|
|
|
|
return g_moonstone_loc_tbl[phase]
|
|
end
|
|
|
|
function update_moongates(show_moongates)
|
|
local i, loc
|
|
for i,loc in ipairs(g_moonstone_loc_tbl) do
|
|
local moongate = map_get_obj(loc.x, loc.y, loc.z, 0x55) --moongate
|
|
if show_moongates == true then
|
|
if moongate == nil and loc.x ~= 0 then
|
|
moongate = Obj.new(0x55, 1)
|
|
Obj.moveToMap(moongate, loc)
|
|
end
|
|
else --hide moongate
|
|
if moongate ~= nil then
|
|
map_remove_obj(moongate)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function use_keg(obj)
|
|
if obj.frame_n ~= 0 then
|
|
print("\nNo effect\n")
|
|
return
|
|
end
|
|
|
|
if g_keg_timer > 0 then
|
|
print("\nNot now\n")
|
|
else
|
|
obj.frame_n = 1
|
|
print("\nPowder lit!\n")
|
|
g_keg_timer = 3
|
|
end
|
|
end
|
|
|
|
function explode_keg()
|
|
--try to find lit keg on the current game map.
|
|
local loc = player_get_location()
|
|
for obj in find_obj(loc.z, 223, 1) do --keg obj, frame_n = 1
|
|
if obj ~= nil then
|
|
explode_obj(obj)
|
|
end
|
|
end
|
|
|
|
--try to explode lit kegs in the party's inventory
|
|
local party_actor
|
|
for party_actor in party_members() do
|
|
local obj = Actor.inv_get_obj_n(party_actor, 223, 1) --keg with frame_n = 1
|
|
if obj ~= nil then
|
|
explode_obj(obj, party_actor)
|
|
end
|
|
end
|
|
end
|
|
|
|
function explode_obj(obj, actor)
|
|
dbg("Exploding "..obj.name.."\n")
|
|
local x, y, z
|
|
|
|
if actor ~= nil then
|
|
x = actor.x
|
|
y = actor.y
|
|
z = actor.z
|
|
else
|
|
x = obj.x
|
|
y = obj.y
|
|
z = obj.z
|
|
end
|
|
|
|
Obj.removeFromEngine(obj)
|
|
|
|
local hit_items = explosion(0x189, x, y)
|
|
local random = math.random
|
|
local k, v
|
|
for k,v in pairs(hit_items) do
|
|
if v.luatype == "actor" then
|
|
actor_hit(v, random(1, 0x3c))
|
|
end
|
|
if g_avatar_died == true then
|
|
explode_surrounding_objects(x, y, z)
|
|
actor_avatar_death(Actor.get(1))
|
|
return -- don't keep exploding once Avatar is dead
|
|
end
|
|
end
|
|
|
|
explode_surrounding_objects(x, y, z)
|
|
end
|
|
|
|
function explode_surrounding_objects(x, y, z)
|
|
--blow up doors and other kegs
|
|
for x = x - 2,x + 2 do
|
|
for y = y - 2,y + 2 do
|
|
local map_obj = map_get_obj(x, y, z, 223)
|
|
if map_obj ~= nil then
|
|
explode_obj(map_obj)
|
|
end
|
|
|
|
map_obj = map_get_obj(x, y, z, 0x12c) --steel door
|
|
if map_obj == nil or map_obj.frame_n == 0xc then
|
|
map_obj = map_get_obj(x, y, z, 0x129) --oaken door
|
|
if map_obj == nil or map_obj.frame_n == 0xc then
|
|
map_obj = map_get_obj(x, y, z, 0x12a) --windowed door
|
|
if map_obj == nil or map_obj.frame_n == 0xc then
|
|
map_obj = map_get_obj(x, y, z, 0x12b) --cedar door
|
|
end
|
|
end
|
|
end
|
|
|
|
if map_obj ~= nil and map_obj.frame_n <= 0xc then
|
|
Obj.removeFromEngine(map_obj)
|
|
print("\nThe door is blown up!\n")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function set_g_armageddon(val)
|
|
g_armageddon = val
|
|
set_armageddon(val)
|
|
end
|
|
|
|
function create_object_needs_quan(obj_n)
|
|
-- obj.stackable is already checked
|
|
return false
|
|
end
|
|
|
|
--load actor functions
|
|
actor_load = nuvie_load("u6/actor.lua");
|
|
if type(actor_load) == "function" then
|
|
actor_load()
|
|
else
|
|
if type(actor_load) == "string" then
|
|
io.stderr:write(actor_load);
|
|
end
|
|
end
|
|
|
|
-- init magic
|
|
magic_init = nuvie_load("u6/magic.lua"); magic_init();
|
|
|
|
-- init usecode
|
|
usecode_init = nuvie_load("u6/usecode.lua"); usecode_init();
|
|
|
|
player_init = nuvie_load("u6/player.lua"); player_init();
|