From 494fd980f7fe808dbc6f7f63d7fe8f59c5b5702c Mon Sep 17 00:00:00 2001 From: server Date: Thu, 16 Apr 2026 18:33:07 +0200 Subject: [PATCH 1/2] issue-5: add teleport quest system --- share/locale/english/quest/locale_list | 1 + .../english/quest/teleport_system.quest | 336 ++++++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 share/locale/english/quest/teleport_system.quest diff --git a/share/locale/english/quest/locale_list b/share/locale/english/quest/locale_list index 71a8c86..4548d82 100644 --- a/share/locale/english/quest/locale_list +++ b/share/locale/english/quest/locale_list @@ -53,6 +53,7 @@ collect_herb_lv25.quest collect_herb_lv4.quest collect_herb_lv7.quest biolog_system.quest +teleport_system.quest couple_ring.quest cube.quest deviltower_zone.quest diff --git a/share/locale/english/quest/teleport_system.quest b/share/locale/english/quest/teleport_system.quest new file mode 100644 index 0000000..2ca9f07 --- /dev/null +++ b/share/locale/english/quest/teleport_system.quest @@ -0,0 +1,336 @@ +quest teleport_system begin + state start begin + function get_base_slot_count() + return 3 + end + + function get_vip_slot_count() + return 5 + end + + function get_cooldown_seconds() + return 30 + end + + function has_vip_slots() + return pc.get_premium_remain_sec(PREMIUM_GOLD) > 0 + end + + function get_max_slot_count() + if teleport_system.has_vip_slots() then + return teleport_system.get_vip_slot_count() + end + + return teleport_system.get_base_slot_count() + end + + function get_restricted_maps() + return { + [71] = true, + [72] = true, + [73] = true, + [104] = true, + [113] = true, + [200] = true, + [208] = true, + [301] = true, + } + end + + function is_map_restricted(map_index) + if map_index == nil or map_index <= 0 then + return true + end + + if map_index > 10000 then + return true + end + + return teleport_system.get_restricted_maps()[map_index] == true + end + + function can_use_now() + if false == pc.can_warp() then + return false, "Teleport is blocked right now." + end + + local map_index = pc.get_map_index() + if pc.in_dungeon() or teleport_system.is_map_restricted(map_index) then + return false, "You cannot use teleport on this map." + end + + if pc.getqf("cooldown_end") > get_time() then + return false, "Teleport is on cooldown." + end + + return true, "" + end + + function get_saved_map(slot) + return pc.getqf("slot_" .. slot .. "_map") + end + + function get_saved_x(slot) + return pc.getqf("slot_" .. slot .. "_x") + end + + function get_saved_y(slot) + return pc.getqf("slot_" .. slot .. "_y") + end + + function has_saved_slot(slot) + return teleport_system.get_saved_map(slot) > 0 + and teleport_system.get_saved_x(slot) > 0 + and teleport_system.get_saved_y(slot) > 0 + end + + function get_saved_slot_count() + local count = 0 + for slot = 1, teleport_system.get_vip_slot_count() do + if teleport_system.has_saved_slot(slot) then + count = count + 1 + end + end + + return count + end + + function clear_slot(slot) + pc.setqf("slot_" .. slot .. "_map", 0) + pc.setqf("slot_" .. slot .. "_x", 0) + pc.setqf("slot_" .. slot .. "_y", 0) + end + + function get_empire_preset(id) + local empire = pc.get_empire() + local presets = { + [1] = { + name = "First Village", + coords = { + [1] = { x = 474300, y = 954800 }, + [2] = { x = 63800, y = 166400 }, + [3] = { x = 959900, y = 269200 }, + }, + }, + [2] = { + name = "Second Village", + coords = { + [1] = { x = 353100, y = 882900 }, + [2] = { x = 145500, y = 240000 }, + [3] = { x = 863900, y = 246000 }, + }, + }, + [3] = { + name = "Valley", + coords = { + [1] = { x = 402100, y = 673900 }, + [2] = { x = 270400, y = 739900 }, + [3] = { x = 321300, y = 808000 }, + }, + }, + [4] = { + name = "Desert", + coords = { + [1] = { x = 217800, y = 627200 }, + [2] = { x = 221900, y = 502700 }, + [3] = { x = 344000, y = 502500 }, + }, + }, + [5] = { + name = "Sohan", + coords = { + [1] = { x = 434200, y = 290600 }, + [2] = { x = 375200, y = 174900 }, + [3] = { x = 491800, y = 173600 }, + }, + }, + [6] = { + name = "Fireland", + coords = { + [1] = { x = 599400, y = 756300 }, + [2] = { x = 597800, y = 622200 }, + [3] = { x = 730700, y = 689800 }, + }, + }, + [7] = { + name = "Devil Tower", + coords = { + [1] = { x = 590500, y = 110500 }, + [2] = { x = 590500, y = 110500 }, + [3] = { x = 590500, y = 110500 }, + }, + }, + [8] = { + name = "Heavens Cave", + coords = { + [1] = { x = 287800, y = 799700 }, + [2] = { x = 275500, y = 800000 }, + [3] = { x = 277000, y = 788000 }, + }, + }, + } + + local preset = presets[id] + if preset == nil then + return nil + end + + local coords = preset.coords[empire] + if coords == nil then + return nil + end + + return { + name = preset.name, + x = coords.x, + y = coords.y, + } + end + + function start_cooldown() + pc.setqf("cooldown_end", get_time() + teleport_system.get_cooldown_seconds()) + end + + function refresh_tracker() + send_letter("Teleport System") + q.set_title("Teleport System") + q.set_counter_name("Saved / " .. teleport_system.get_max_slot_count()) + q.set_counter_value(teleport_system.get_saved_slot_count()) + + local cooldown = pc.getqf("cooldown_end") - get_time() + if cooldown > 0 then + q.set_clock("Cooldown", cooldown) + else + q.set_clock_name("") + end + + q.start() + end + + function show_status_dialog() + local cooldown = pc.getqf("cooldown_end") - get_time() + if cooldown < 0 then + cooldown = 0 + end + + say_title("Teleport System") + say("Use the HUD teleport button near the minimap.") + say("") + say_reward(string.format("Saved slots: %d / %d", teleport_system.get_saved_slot_count(), teleport_system.get_max_slot_count())) + + if cooldown > 0 then + say_reward(string.format("Cooldown: %d seconds", cooldown)) + else + say_reward("Cooldown: Ready") + end + end + + function handle_save(slot) + if slot < 1 or slot > teleport_system.get_max_slot_count() then + syschat("This slot is locked.") + return + end + + local map_index = pc.get_map_index() + if false == pc.can_warp() then + syschat("Teleport is blocked right now.") + return + end + + if pc.in_dungeon() or teleport_system.is_map_restricted(map_index) then + syschat("This map cannot be saved.") + return + end + + pc.setqf("slot_" .. slot .. "_map", map_index) + pc.setqf("slot_" .. slot .. "_x", pc.get_x()) + pc.setqf("slot_" .. slot .. "_y", pc.get_y()) + syschat(string.format("Saved current position to slot %d.", slot)) + end + + function handle_saved_warp(slot) + if slot < 1 or slot > teleport_system.get_max_slot_count() then + syschat("This slot is locked.") + return + end + + if not teleport_system.has_saved_slot(slot) then + syschat("This slot is empty.") + return + end + + local can_use, err = teleport_system.can_use_now() + if not can_use then + syschat(err) + return + end + + local map_index = teleport_system.get_saved_map(slot) + if teleport_system.is_map_restricted(map_index) then + syschat("This saved position is no longer available.") + teleport_system.clear_slot(slot) + return + end + + teleport_system.start_cooldown() + pc.warp_local(map_index, teleport_system.get_saved_x(slot), teleport_system.get_saved_y(slot)) + end + + function handle_preset_warp(preset_id) + local preset = teleport_system.get_empire_preset(preset_id) + if preset == nil then + syschat("Unknown teleport preset.") + return + end + + local can_use, err = teleport_system.can_use_now() + if not can_use then + syschat(err) + return + end + + teleport_system.start_cooldown() + pc.warp(preset.x, preset.y) + end + + function handle_remote_action() + local action = pc.getqf("remote_action") + local arg = pc.getqf("remote_arg") + + pc.setqf("remote_action", 0) + pc.setqf("remote_arg", 0) + + if action == 1 then + teleport_system.handle_save(arg) + elseif action == 2 then + teleport_system.handle_saved_warp(arg) + elseif action == 3 then + teleport_system.handle_preset_warp(arg) + end + end + + when login or enter begin + teleport_system.refresh_tracker() + end + + when letter begin + teleport_system.refresh_tracker() + end + + when button begin + if pc.getqf("remote_action") != 0 then + teleport_system.handle_remote_action() + teleport_system.refresh_tracker() + return + end + + teleport_system.show_status_dialog() + teleport_system.refresh_tracker() + end + + when info begin + teleport_system.show_status_dialog() + teleport_system.refresh_tracker() + end + end +end From 142957f5452d1e54a59493cf58bc2279037b0b1d Mon Sep 17 00:00:00 2001 From: server Date: Thu, 16 Apr 2026 21:08:13 +0200 Subject: [PATCH 2/2] issue-9: add sash item proto template --- sql/issue_9_sashes.sql | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 sql/issue_9_sashes.sql diff --git a/sql/issue_9_sashes.sql b/sql/issue_9_sashes.sql new file mode 100644 index 0000000..132cc58 --- /dev/null +++ b/sql/issue_9_sashes.sql @@ -0,0 +1,35 @@ +-- Issue #9: classic sash support +-- value0 = absorb percent +-- subtype = COSTUME_SASH +-- wearflag can stay 0 for ITEM_COSTUME, but WEARABLE_COSTUME_SASH is available if you want parity in tools + +-- Sample classic tiers +-- Adjust icon/model names and vnums to your asset pack. + +INSERT INTO item_proto + (vnum, name, locale_name, type, subtype, size, antiflag, flag, wearflag, immuneflag, gold, shop_buy_price, + limittype0, limitvalue0, limittype1, limitvalue1, + applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, + value0, value1, value2, value3, value4, value5, + socket0, socket1, socket2, refined_vnum, refine_set, magic_pct, specular, socket_pct) +VALUES + (85000, 'sash_common', 'Common Sash', 28, 2, 2, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0), + (85010, 'sash_rare', 'Rare Sash', 28, 2, 2, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0), + (85020, 'sash_epic', 'Epic Sash', 28, 2, 2, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 15, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0), + (85030, 'sash_legendary', 'Legendary Sash', 28, 2, 2, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 20, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0);