µ±Ä¬Èϸ±±¾ÍæÄ壬Դ´ÄÚÈݳÉΪ²îÒ컯µÄºËÐÄÀûÆ÷¡£µ«Ãæ¶Ô·â±ÕµÄ·þÎñ¶Ë´úÂ룬ÈçºÎÉè¼ÆÐ¸±±¾£¿ÔõÑùʵÏÖÔÚÏßʼþ£¿±¾Æª½ÒÃÜÈÈѪ½ºþµØÍ¼´´½¨¡¢¹ÖÎïAI±à¼¡¢ÈÎÎñÁ´¹¹½¨µÄʵսÁ÷³Ì£¬½ÌÄãʵÏÖ¼¼Êõ²ãÃæµÄ“¿ª×ÚÁ¢ÅÉ”¡£
⚠️ ¿ª·¢Â×ÀíÓëÄÜÁ¦±ß½ç
·´±àÒë·çÏÕ£ºC++·þÎñ¶ËÐÞ¸ÄÐè·´±àÒ루·¨ÂÉ»ÒÉ«µØ´ø£¬±¾ÎIJ»Éæ¼°£©
ÓÐÏÞ´´Ô죺80%Ð޸ĻùÓÚÅäÖÃÎļþºÍ½Å±¾×¢È루Lua/Python£©
°æÈ¨¸ôÀ룺ËùÓÐÃÀÊõ×ÊÔ´/µØÃûÐèÖØÐÂÃüÃû£¨±ÜÃâÖ±½ÓÇÖȨ£©
---
🗺️ Ò»¡¢ÐµØÍ¼´î½¨ËIJ½·¨£¨ÎÞÐèÔ´ÂëÐ޸ģ©
²½Öè1£ºµØÐÎÎļþ¿Ë¡ÓëÖØ¶¨Ïò
·þÎñ¶ËĿ¼½á¹¹
©À©¤ map
©À©¤ origin£¨Ô°æµØÍ¼Îļþ¼Ð£©
©¦ ©À©¤ forest01.map # ÉÁÖµØÍ¼Îļþ
©¦ ©¸©¤ forest01.obj # µØÍ¼ÅöײÌå
©¦
©¸©¤ my_dungeon # н¨¸±±¾Ä¿Â¼
©À©¤ volcano.map # ¸´ÖÆforest01.mapÖØÃüÃû
©¸©¤ volcano.obj # ¸´ÖÆforest01.objÖØÃüÃû
ÉúЧ¹Ø¼ü£ºÔÚ map_index.conf ÖÐÌí¼ÓÓ³Éä
ÔµØÍ¼ID=1000 ÐÂID=9100
[MapInfo]
1000 = forest01 # ±£ÁôÔµØÍ¼
9100 = my_dungeon/volcano # Ö¸ÏòÐÂÎļþ
²½Öè2£ºÊÓ¾õ×ÊÔ´Ìæ»»£¨¿Í»§¶Ë²ãÃæ£©
Óà Photoshop + ½ºþ×ÊÔ´½â°ü¹¤¾ß ÐÞ¸ÄÌùͼ
½«forest01.tgaÖеÄÊ÷Ä¾Ìæ»»ÎªÈÛÑÒÎÆÀí
Ìæ»»¿Í»§¶Ë data/map/my_dungeon ÖеÄÌùͼÎļþ
×¢£ºÐèͬ²½¸üпͻ§¶ËµÄ²¹¶¡°ü
²½Öè3£ºÉèÖô«Ë͵㣨Êý¾Ý¿â+½Å±¾£©
ÔÚ t_warpgates ±íÌí¼ÓÐÂÌõÄ¿£º
×Ö¶Î Öµ ˵Ã÷
FromMap 45 Ö÷³ÇµØÍ¼ID
FromPos (123, 456) ´«ËÍÕó×ø±ê
ToMap 9100 Ä¿±ê¸±±¾ID
ToPos (50, 50) ¸±±¾³öÉúµã
²½Öè4£º¶¯Ì¬Õϰ¿ØÖÆ£¨Ê±¼ä/ʼþ´¥·¢£©
-- ÈÛÑÒ¸±±¾Æô¶¯ºó×èÈûÈë¿Ú
local block_pos = {x=50, y=52}
function onEventStart()
map:spawnBlock(block_pos) -- Éú³ÉÕϰÎï
end
function onEventFinish()
map:removeBlock(block_pos) -- ʼþ½áÊø½â³ý
end
🤖 ¶þ¡¢ÖÇÄܹÖÎ↑·¢£ºÈÃBOSSÓµÓÐÕ½ÊõÐÐΪ
»ù´¡ÊôÐÔÅäÖã¨t_mobs ±í£©
INSERT INTO t_mobs (MobID, Name, HP, Attack, AI_Type)
VALUES (9001, 'ÈÛÑÒ¾ÞÊÞ', 500000, 2500, 4); -- AI_Type=4±íʾ¸ß¼¶AI
×Ô¶¨ÒåAIÂß¼Ê÷£¨Lua½Å±¾ÊµÏÖ£©
-- ai_custom/lava_boss.lua
function onBossCombat(boss, target)
-- ½×¶ÎÅжϣºÑªÁ¿·Ö¶Î´¥·¢¼¼ÄÜ
local hp_percent = boss:getHpPercent()
if hp_percent > 70 then
boss:castAOE(302, 10) -- »ð»·¼¼ÄÜID 302£¬·¶Î§10Ã×
elseif hp_percent > 30 then
boss:summon(91001, 3) -- ÕÙ»½3Ö»ÈÛÑÒС¹Ö£¨ID=91001£©
boss:castDebuff(target, 15) -- Ê©¼Ó¼õ·ÀDEBUFF
else -- ¿ñ±©½×¶Î
boss:setAttackSpeed(200) -- ¹¥ËÙ·±¶
boss:castDOT(305) -- È«ÆÁ³ÖÐøÉ˺¦
end
end
AI¹ÒÔØÅäÖãºÔÚ ai_loader.conf ×¢²á
[MobAIScript]
9001 = ai_custom/lava_boss.lua # BossID¹ØÁª½Å±¾
🔗 Èý¡¢Ê·Ê«ÈÎÎñÁ´£ºÇ¶Ì׸±±¾+¶à½×¶Î½»»¥
Êý¾Ý¿âÈÎÎñÉè¼Æ£¨t_quests±í£©
ÈÎÎñ½×¶Î ÈÎÎñÄ¿±ê ¸±±¾¹ØÁª
½×¶Î1 ÊÕ¼¯3·ÝÈÛÑÒÑù±¾ ²É¼¯¸±±¾£¨9100µØÍ¼£©
½×¶Î2 »÷°ÜÈÛÑÒ¾ÞÊÞ Õ½¶·¸±±¾£¨µØÍ¼ID 9100£©
½×¶Î3 ¾»»¯ºËÐÄ ÏÞʱÌÓÍѸ±±¾£¨µØÍ¼ID 9101£©
½×¶Î¿ØÖƽű¾Ê¾Àý
-- ½×¶Î2Íê³Éºó¿ªÆôÏÞʱÌÓÍÑ
function onQuestStageComplete(player, quest_id, stage)
if quest_id 9001 and stage 2 then
player:teleport(9101, 20, 20) -- ´«ËÍÖÁи±±¾
player:startTimer(300, "µ¹¼ÆÊ±½áÊø´«Ëͳö") -- 5·ÖÖÓÏÞʱ
end
end
-- ¶¨Ê±Æ÷»Øµ÷
function onTimer(player, timer_name)
if timer_name == "µ¹¼ÆÊ±½áÊø´«Ëͳö" then
player:teleport(45, 130, 80) -- »ØÖ÷³Ç
player:failQuest(9001) -- ÈÎÎñʧ°Ü
end
end
🧪 ËÄ¡¢¼´Ê±Ê¼þϵͳ£ºÈ«·þ¶¯Ì¬»î¶¯¿ª·¢
ÔÚÏßʼþ¹ÜÀíÆ÷£¨Pythonα´úÂ룩
event_server.py
def run_carnival_event():
# 1. È«·þ¹«¸æ
broadcast("ÈÛÑÒÇìµäÒÑ¿ªÆô£¡")
# 2. ÁÙʱ¸±±¾¼¤»î
enable_map(9100)
# 3. Éú³É»î¶¯NPC
spawn_npc(map_id=45, npc_id=3009, pos=(100,100))
»ùÓÚ¶¨Ê±ÈÎÎñ´¥·¢
scheduler.add_job(
run_carnival_event,
'cron',
day_of_week='sat,sun',
hour=20
)
¿Í»§¶ËʼþÌáʾ·½°¸
<!-- ¿Í»§¶Ë½çÃæÐÞ¸Ä Client/UI/event_notice.xml -->
<Panel name="EventNotice">
<Text name="msg" x="50" y="20" font="bold"/>
<Timer name="countdown" visible="false"/>
</Panel>
-- LuaÏìÓ¦·þÎñ¶Ëʼþ¹ã²¥
function onServerEventStart(event_name)
UI.EventNotice.msg:setText(event_name)
UI.EventNotice:show()
end
🛡️ Îå¡¢±Ü¿ÓÖ¸ÄÏ£ºÔ´´ÄÚÈÝ¿ª·¢Ê®´óÏÝÚå
Îļþ±àÂë²»Ò»Ö → ËùÓÐÅäÖÃÓà UTF-8 without BOM
µØÍ¼ID³åÍ» → ¿ª·¢×¨ÓÃID¶Î£¨9000~9999£©
¿Í»§¶Ë¿¨ËÀ → еØÍ¼±ØÐëÌá½»²¹¶¡°ü£¨Íæ¼ÒÐè¸üУ©
AIËÀÑ»·¹¥»÷ → ½Å±¾ÄÚÌí¼ÓÐÐΪÀäÈ´CD
ÄÚ´æÐ¹Â¶Ô¤¾¯ → ÿ24Ð¡Ê±ÖØÆô·þÎñ¶ËÊÍ·Å×ÊÔ´
💎 ÖÕ¼«ÖҸ棺¼¼ÊõÎÞ×±ß½çÔÚÐÄ
Ô´´¸±±¾¿ª·¢ÊǼ¼ÊõÓë´´ÒâµÄ»ªÀö¹²Î裬µ«ÇëʼÖÕÇåÐÑ£º
ÄãµÄLua½Å±¾Ô½¾«ÃԽÐèÃæ¶Ô·¨Âɱ߽çµÄ¿½ÎÊ
Íæ¼ÒÒòÄãµÄ´´Ôì¶ø»¶ºôʱ£¬ÕæÕýµÄ½ºþÕýÔÚÕý°æ·þÎñÆ÷ÖÐÉúÉú²»Ï¢
µ±´úÂëÄÜÁ¦×ã¹»¼ÝÔ¦´´ÔìÖ®ÄÜ£¬ÉÌÒµÓÎÏ·¿ª·¢»òÐí²ÅÊǸü¹ãÀ«µÄ½ºþ¡£
🔧 ¸½£º¿ª·¢¹¤¾ß°×Ãûµ¥
µØÍ¼±à¼Æ÷£ºTiled (ÐÞ¸Ä.mapÎļþ)
½Å±¾µ÷ÊÔ£ºZeroBrane Studio (Lua IDE)
Êý¾Ý¼à¿Ø£ºNavicat Premium + Êý¾Ý¿â¿ìÕÕ
°²È«¸ôÀ룺VMwareÐéÄâ»ú + ÿÈÕ¿ìÕÕ
⚠️ ¿ª·¢Â×ÀíÓëÄÜÁ¦±ß½ç
·´±àÒë·çÏÕ£ºC++·þÎñ¶ËÐÞ¸ÄÐè·´±àÒ루·¨ÂÉ»ÒÉ«µØ´ø£¬±¾ÎIJ»Éæ¼°£©
ÓÐÏÞ´´Ô죺80%Ð޸ĻùÓÚÅäÖÃÎļþºÍ½Å±¾×¢È루Lua/Python£©
°æÈ¨¸ôÀ룺ËùÓÐÃÀÊõ×ÊÔ´/µØÃûÐèÖØÐÂÃüÃû£¨±ÜÃâÖ±½ÓÇÖȨ£©
---
🗺️ Ò»¡¢ÐµØÍ¼´î½¨ËIJ½·¨£¨ÎÞÐèÔ´ÂëÐ޸ģ©
²½Öè1£ºµØÐÎÎļþ¿Ë¡ÓëÖØ¶¨Ïò
·þÎñ¶ËĿ¼½á¹¹
©À©¤ map
©À©¤ origin£¨Ô°æµØÍ¼Îļþ¼Ð£©
©¦ ©À©¤ forest01.map # ÉÁÖµØÍ¼Îļþ
©¦ ©¸©¤ forest01.obj # µØÍ¼ÅöײÌå
©¦
©¸©¤ my_dungeon # н¨¸±±¾Ä¿Â¼
©À©¤ volcano.map # ¸´ÖÆforest01.mapÖØÃüÃû
©¸©¤ volcano.obj # ¸´ÖÆforest01.objÖØÃüÃû
ÉúЧ¹Ø¼ü£ºÔÚ map_index.conf ÖÐÌí¼ÓÓ³Éä
ÔµØÍ¼ID=1000 ÐÂID=9100
[MapInfo]
1000 = forest01 # ±£ÁôÔµØÍ¼
9100 = my_dungeon/volcano # Ö¸ÏòÐÂÎļþ
²½Öè2£ºÊÓ¾õ×ÊÔ´Ìæ»»£¨¿Í»§¶Ë²ãÃæ£©
Óà Photoshop + ½ºþ×ÊÔ´½â°ü¹¤¾ß ÐÞ¸ÄÌùͼ
½«forest01.tgaÖеÄÊ÷Ä¾Ìæ»»ÎªÈÛÑÒÎÆÀí
Ìæ»»¿Í»§¶Ë data/map/my_dungeon ÖеÄÌùͼÎļþ
×¢£ºÐèͬ²½¸üпͻ§¶ËµÄ²¹¶¡°ü
²½Öè3£ºÉèÖô«Ë͵㣨Êý¾Ý¿â+½Å±¾£©
ÔÚ t_warpgates ±íÌí¼ÓÐÂÌõÄ¿£º
×Ö¶Î Öµ ˵Ã÷
FromMap 45 Ö÷³ÇµØÍ¼ID
FromPos (123, 456) ´«ËÍÕó×ø±ê
ToMap 9100 Ä¿±ê¸±±¾ID
ToPos (50, 50) ¸±±¾³öÉúµã
²½Öè4£º¶¯Ì¬Õϰ¿ØÖÆ£¨Ê±¼ä/ʼþ´¥·¢£©
-- ÈÛÑÒ¸±±¾Æô¶¯ºó×èÈûÈë¿Ú
local block_pos = {x=50, y=52}
function onEventStart()
map:spawnBlock(block_pos) -- Éú³ÉÕϰÎï
end
function onEventFinish()
map:removeBlock(block_pos) -- ʼþ½áÊø½â³ý
end
🤖 ¶þ¡¢ÖÇÄܹÖÎ↑·¢£ºÈÃBOSSÓµÓÐÕ½ÊõÐÐΪ
»ù´¡ÊôÐÔÅäÖã¨t_mobs ±í£©
INSERT INTO t_mobs (MobID, Name, HP, Attack, AI_Type)
VALUES (9001, 'ÈÛÑÒ¾ÞÊÞ', 500000, 2500, 4); -- AI_Type=4±íʾ¸ß¼¶AI
×Ô¶¨ÒåAIÂß¼Ê÷£¨Lua½Å±¾ÊµÏÖ£©
-- ai_custom/lava_boss.lua
function onBossCombat(boss, target)
-- ½×¶ÎÅжϣºÑªÁ¿·Ö¶Î´¥·¢¼¼ÄÜ
local hp_percent = boss:getHpPercent()
if hp_percent > 70 then
boss:castAOE(302, 10) -- »ð»·¼¼ÄÜID 302£¬·¶Î§10Ã×
elseif hp_percent > 30 then
boss:summon(91001, 3) -- ÕÙ»½3Ö»ÈÛÑÒС¹Ö£¨ID=91001£©
boss:castDebuff(target, 15) -- Ê©¼Ó¼õ·ÀDEBUFF
else -- ¿ñ±©½×¶Î
boss:setAttackSpeed(200) -- ¹¥ËÙ·±¶
boss:castDOT(305) -- È«ÆÁ³ÖÐøÉ˺¦
end
end
AI¹ÒÔØÅäÖãºÔÚ ai_loader.conf ×¢²á
[MobAIScript]
9001 = ai_custom/lava_boss.lua # BossID¹ØÁª½Å±¾
🔗 Èý¡¢Ê·Ê«ÈÎÎñÁ´£ºÇ¶Ì׸±±¾+¶à½×¶Î½»»¥
Êý¾Ý¿âÈÎÎñÉè¼Æ£¨t_quests±í£©
ÈÎÎñ½×¶Î ÈÎÎñÄ¿±ê ¸±±¾¹ØÁª
½×¶Î1 ÊÕ¼¯3·ÝÈÛÑÒÑù±¾ ²É¼¯¸±±¾£¨9100µØÍ¼£©
½×¶Î2 »÷°ÜÈÛÑÒ¾ÞÊÞ Õ½¶·¸±±¾£¨µØÍ¼ID 9100£©
½×¶Î3 ¾»»¯ºËÐÄ ÏÞʱÌÓÍѸ±±¾£¨µØÍ¼ID 9101£©
½×¶Î¿ØÖƽű¾Ê¾Àý
-- ½×¶Î2Íê³Éºó¿ªÆôÏÞʱÌÓÍÑ
function onQuestStageComplete(player, quest_id, stage)
if quest_id 9001 and stage 2 then
player:teleport(9101, 20, 20) -- ´«ËÍÖÁи±±¾
player:startTimer(300, "µ¹¼ÆÊ±½áÊø´«Ëͳö") -- 5·ÖÖÓÏÞʱ
end
end
-- ¶¨Ê±Æ÷»Øµ÷
function onTimer(player, timer_name)
if timer_name == "µ¹¼ÆÊ±½áÊø´«Ëͳö" then
player:teleport(45, 130, 80) -- »ØÖ÷³Ç
player:failQuest(9001) -- ÈÎÎñʧ°Ü
end
end
🧪 ËÄ¡¢¼´Ê±Ê¼þϵͳ£ºÈ«·þ¶¯Ì¬»î¶¯¿ª·¢
ÔÚÏßʼþ¹ÜÀíÆ÷£¨Pythonα´úÂ룩
event_server.py
def run_carnival_event():
# 1. È«·þ¹«¸æ
broadcast("ÈÛÑÒÇìµäÒÑ¿ªÆô£¡")
# 2. ÁÙʱ¸±±¾¼¤»î
enable_map(9100)
# 3. Éú³É»î¶¯NPC
spawn_npc(map_id=45, npc_id=3009, pos=(100,100))
»ùÓÚ¶¨Ê±ÈÎÎñ´¥·¢
scheduler.add_job(
run_carnival_event,
'cron',
day_of_week='sat,sun',
hour=20
)
¿Í»§¶ËʼþÌáʾ·½°¸
<!-- ¿Í»§¶Ë½çÃæÐÞ¸Ä Client/UI/event_notice.xml -->
<Panel name="EventNotice">
<Text name="msg" x="50" y="20" font="bold"/>
<Timer name="countdown" visible="false"/>
</Panel>
-- LuaÏìÓ¦·þÎñ¶Ëʼþ¹ã²¥
function onServerEventStart(event_name)
UI.EventNotice.msg:setText(event_name)
UI.EventNotice:show()
end
🛡️ Îå¡¢±Ü¿ÓÖ¸ÄÏ£ºÔ´´ÄÚÈÝ¿ª·¢Ê®´óÏÝÚå
Îļþ±àÂë²»Ò»Ö → ËùÓÐÅäÖÃÓà UTF-8 without BOM
µØÍ¼ID³åÍ» → ¿ª·¢×¨ÓÃID¶Î£¨9000~9999£©
¿Í»§¶Ë¿¨ËÀ → еØÍ¼±ØÐëÌá½»²¹¶¡°ü£¨Íæ¼ÒÐè¸üУ©
AIËÀÑ»·¹¥»÷ → ½Å±¾ÄÚÌí¼ÓÐÐΪÀäÈ´CD
ÄÚ´æÐ¹Â¶Ô¤¾¯ → ÿ24Ð¡Ê±ÖØÆô·þÎñ¶ËÊÍ·Å×ÊÔ´
💎 ÖÕ¼«ÖҸ棺¼¼ÊõÎÞ×±ß½çÔÚÐÄ
Ô´´¸±±¾¿ª·¢ÊǼ¼ÊõÓë´´ÒâµÄ»ªÀö¹²Î裬µ«ÇëʼÖÕÇåÐÑ£º
ÄãµÄLua½Å±¾Ô½¾«ÃԽÐèÃæ¶Ô·¨Âɱ߽çµÄ¿½ÎÊ
Íæ¼ÒÒòÄãµÄ´´Ôì¶ø»¶ºôʱ£¬ÕæÕýµÄ½ºþÕýÔÚÕý°æ·þÎñÆ÷ÖÐÉúÉú²»Ï¢
µ±´úÂëÄÜÁ¦×ã¹»¼ÝÔ¦´´ÔìÖ®ÄÜ£¬ÉÌÒµÓÎÏ·¿ª·¢»òÐí²ÅÊǸü¹ãÀ«µÄ½ºþ¡£
🔧 ¸½£º¿ª·¢¹¤¾ß°×Ãûµ¥
µØÍ¼±à¼Æ÷£ºTiled (ÐÞ¸Ä.mapÎļþ)
½Å±¾µ÷ÊÔ£ºZeroBrane Studio (Lua IDE)
Êý¾Ý¼à¿Ø£ºNavicat Premium + Êý¾Ý¿â¿ìÕÕ
°²È«¸ôÀ룺VMwareÐéÄâ»ú + ÿÈÕ¿ìÕÕ

