这个世界有一个叫做“世界心脏(Heart Of World)”的唯一全局对象,所有在游戏中,会随着时间变化的对象,都需要通过 Add() 方法把自己加入这个对象;在对象“死去”的时候,用 Del() 方法去掉自己的引用。一旦“加入了”世界心脏后,这些对象的 HartBeat() 心跳方法就会跟随“世界心脏”定期跳动,所有的对象需要不断运行的行为,都可以放入自己的心跳方法里。
--- Timer SystemHeartOfWorld = { rate = 1, -- beat times per second members = {}, -- all hearts in here last_beat_time = 0}function HeartOfWorld:Add(heart) ......endfunction HeartOfWorld:Del(heart_or_idx) ......endfunction HeartOfWorld:Tick() ......-- Make hearts beating for idx, obj in pairs(self.members) do if obj.HeartBeat ~= nil and type(obj.HeartBeat) == 'function' then obj:HeartBeat(now) end end ......end
--- Broadcast systemChannel = { members = {}}......function Channel:Join(user_id, member)...endfunction Channel:Leave(user_id)...endfunction Channel:Say(message, ...) for user_id, member in pairs(self.members) do local ignore = false for i, sender in ipairs { ... } do if member == sender then ignore = true end end if ignore == false then TcpServer:SendTo(user_id, message) end endend
BornPoint = Room:New({ title = "出生点", desc = "这里是一片空地,周围站着很多刚注册的新手玩家。", exits = { east = "NewbiePlaza", west = "SmallRoad" }})NewbiePlaza = Room:New({ title = "新手广场", desc = "光秃秃的黄土地上,有几棵小树。", exits = { west = "BornPoint" }})SmallRoad = Room:New({ title = "小路", desc = "这条小路荒草蔓延。似乎是通往外界的唯一道路。", exits = { east = "BornPoint" }})
角色
一个游戏里面当然需要人,一般包括两类:
NPC
Player
因此我也设计了两个类型,一个叫 Char(角色),一个叫 Player。其中 Player “继承”于 Char。对于角色来说,设计了以下几个方法:
新建/消失
说话。调用当前场景的 channel 进行广播。
移动。进入当前场景,并且会广播进入的动作。
描述。当角色被观察时,把角色的描述、状态进行返回。固定描述用 Desc() 返回。
心跳。这是最重要的方法,所有角色存在的“状态”,都需要在这个方法中描述。这里实现了最基本的“战斗状态”:只要发现了被标记为“敌人”的角色,就调用“战斗系统”发起攻击。 对于 Player 类型,除了上述的内容以外,还有自己的存档对象 user_data,以及专门给玩家单人发送信息的方法 Reply()。
MudLib/char.lua
行为
整个游戏最复杂的部分就是行为部分,这是由一系列的“命令”组成的。一部分是游戏最基本的命令:
登录
注册
登出
移动
观察
说话
基本上可以认为是一个聊天室的功能。这部分能力实现在 MudLib/cmds.lua 当中。
而具体游戏中的额外的命令,则可以通过 MudLib/Cmd/XXX.lua 进行添加,现在包括了:
hp 状态查看
kill 触发战斗
skill 使用技能
虽然游戏命令非常少,但是已经可以构造一个基本的,带技能的战斗玩法。
CommandList.kill = function(cmds) local target = nil local target_id = cmds[2] -- cmds[1]是指令本身,cmds[2]才是参数 if target_id == nil then Reply("你怒气冲冲的瞪着空气,不知道要攻击谁。") return else if target_id == THIS_PLAYER.id then Reply("你狠狠用左脚踢了一下自己的右脚,发现这个行为很傻,于是就停止了。") return end local targets = THIS_PLAYER.environment:Search('id', target_id) if #targets == 0 then Reply(string.format("没有%s这个东西", target_id)) return elseif targets[1].hp ~= nil and targets[1].hp > 0 then target = targets[1] else Reply("你不能攻击一个死物。") return end end if target ~= nil then table.insert(THIS_PLAYER.fright_list, target) Reply(string.format("你对着%s大喝一声:“納命来!”", target.name))--反击 table.insert(target.fright_list, THIS_PLAYER) Reply(string.format("%s对你一瞪眼,一跺脚,狠狠道:“竟敢在太岁头上动土?”", target.name)) if target.user_id ~= nil then target:Reply(string.format("%s向你发起了攻击!", THIS_PLAYER.name)) end endend
if a_skill ~= nil then-- 有招攻无招 if d_skill == nil then damage = double_power else--这种复杂判断其实应该用哈系表查询,但是if写法更容易表达内在含义--tiger>monkey>crane>tiger if a_skill == d_skill then damage = normal_power elseif a_skill == "tiger" then if d_skill == "monkey" then damage = double_power elseif d_skill == "crane" then damage = lease_power end elseif a_skill == "monkey" then if d_skill == "tiger" then damage = lease_power elseif d_skill == "crane" then damage = double_power end elseif a_skill == "crane" then if d_skill == "monkey" then damage = lease_power elseif d_skill == "tiger" then damage = double_power end end end end
评论区
共 2 条评论热门最新