[Moeng-cvs] CaveAdventure/data/scripts AnimationFunctions.lua,NONE,1.1 Boss.lua,NONE,1.1 CaveLang.lu
Status: Alpha
Brought to you by:
b_lindeijer
Update of /cvsroot/moeng/CaveAdventure/data/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv30243 Modified Files: ActionController.lua Actor.lua Animation.lua Controller.lua Decoration.lua EnemySpider.lua Fence.lua Object.lua PlayerController.lua QueenController.lua Added Files: AnimationFunctions.lua Boss.lua CaveLang.lua CaveObjects.lua CaveTriggers.lua Caveman.lua ConversationWindow.lua DgeMapSwitches.lua Dummy.lua Game.lua Guard.lua IngameMenu.lua Interaction.lua InteractionMaster.lua LanguageMenu.lua MainMenu.lua Map.lua MusicController.lua PlayerSwitcher.lua Portal.lua Prisoners.lua Radio.lua Shadow.lua Sound.lua TextProvider.lua Log Message: A lot of more scripts. --- NEW FILE: AnimationFunctions.lua --- -- -- Animation functions to help extracting animations from bitmaps -- -- -- Creates subbitmaps and puts them in an array in the following structure: -- -- {u1, l1, r1, d1, -- u2, l2, r2, d2, -- u3, l3, r3, d3, -- ua, la, ra, da} -- -- Expecting the following bitmap structure: -- -- {d1, d2, d3, da, -- u1, u2, u3, ua, -- r1, r2, r3, ra, -- l1, l2, l3, la} -- function extr_char_anim(bm, w, h) -- Function shortcut local csb = m_create_sub_bitmap if (not bm) then m_message("extr_char_anim(): no bitmap!"); return; end if (not w) then w = 24 end if (not h) then h = 48 end return { csb(bm, 0, h, w, h), csb(bm, 0, h*3, w, h), csb(bm, 0, h*2, w, h), csb(bm, 0, 0, w, h), csb(bm, w, h, w, h), csb(bm, w, h*3, w, h), csb(bm, w, h*2, w, h), csb(bm, w, 0, w, h), csb(bm, w*2, h, w, h), csb(bm, w*2, h*3, w, h), csb(bm, w*2, h*2, w, h), csb(bm, w*2, 0, w, h), csb(bm, w*3, h, w, h), csb(bm, w*3, h*3, w, h), csb(bm, w*3, h*2, w, h), csb(bm, w*3, 0, w, h), } end -- -- Creates an array of subbitmaps from a given bitmap and given the size -- of each individual frame. Frames are extracted from the bitmap like: -- -- {1, 2, 3, 4, -- 5, 6, 7, 8} -- function extr_array(bm, w, h, spacing) if (not bm) then m_message("extr_array(): no bitmap!"); return; end local bw, bh = m_bitmap_size(bm) local bm_array = {} if (not w) then w = bw end if (not h) then h = bh end if (not spacing) then spacing = 0 end for y = 0, (bh / h) - 1 do for x = 0, (bw / w) - 1 do table.insert(bm_array, m_create_sub_bitmap(bm, x*(w+spacing), y*(h+spacing), w, h)) end end return bm_array end --- NEW FILE: Boss.lua --- -- boss.lua -- The big fat green boss import("Character.lua") -- -- The behaving boss with normal sit-in-chair mode -- CaveBoss = Character:subclass { name = "Boss"; defaultproperties = { draw_mode = DM_MASKED, speed = 3, bitmap = m_get_bitmap("boss_1.bmp"), }; } -- -- The super evil, hovering, boss -- EnemyBoss = Character:subclass { name = "EnemyBoss"; init = function(self) inherit(self, BasicChar) inherit(self, BossAI) self:event_init(self) -- Set our own common options self.attack_range = 2 -- nvt self.attack_min_damage = 0 self.attack_max_damage = 0 self.draw_mode = DM_MASKED self.speed = 3 self.bitmap = m_get_bitmap("boss_1.bmp") end; attack = function(self, obj) --ShootPoison(self, obj) end; died = function(self) self.animation = nil --self.bitmap = m_get_bitmap("spider2_dead.bmp") -- -- BIG BOSS DIES SEQUENCE ADDED HERE!!! -- ActionController:addSequence({ ActionAddSequence({ ActionSetVariable(self, "draw_mode", DM_TRANS), ActionSetVariable(self, "goal_z", 0), ActionTweenVariable(self, "alpha", 25, 255), }), ActionWait(50), ActionConversation(lang:getConv("BOSS_BEATEN_1")), ActionExModeOn(), ActionWait(25), ActionTweenVariable(self, "alpha", 200, 0), ActionDestroyObject(self), ActionWait(25), ActionChangeDirection(m_get_player(), DIR_DOWN), ActionConversation(lang:getConv("BOSS_BEATEN_2")), ActionFadeOutMap(100), ActionSetPosition(m_get_player(), 35, 11, DIR_DOWN), ActionSetVariable(m_get_player(), "tick_time", 0), -- DISABLELNG PLAYER TICK! ActionChangeBitmap(m_get_player(), m_get_bitmap("frode_sit1.tga")), ActionSetPosition(cavem1, 33, 11, DIR_RIGHT), ActionSetPosition(cavem2, 37, 11, DIR_LEFT), ActionSetPosition(cavem3, 34, 13, DIR_UP), ActionSetPosition(cavem4, 35, 14, DIR_UP), ActionSetPosition(cavem5, 36, 13, DIR_UP), ActionSetPosition(cavem6, 35, 13, DIR_UP), ActionFadeInMap(100), ActionConversation(lang:getConv("BOSS_BEATEN_3")), ActionFadeOutMap(300), ActionWait(100), ActionQuitGame(), }) end; } -- -- The trigger that sets the boss on the loose -- BossFightTrigger = Actor:subclass { name = "BossFightTrigger"; event_stand_on = function(self, obj) local player = m_get_player() if (player == obj and not boss_triggered) then boss_triggered = 1 ActionController:addSequence({ ActionExModeOn(), ActionSetVariable(camera, "target", self), ActionChangeDirection(player, DIR_UP), ActionConversation(lang:getConv("BOSS_AFTER_PRISON")), ActionAddObject("EnemyBoss", static_boss.x, static_boss.y), ActionDestroyObject(static_boss), ActionSetVariable(boss_block, "obstacle", 1), ActionExModeOff(), }) end end; defaultproperties = { obstacle = 0, w = 1, h = 1, }; } -- -- The boss shadow -- BossShadow = Shadow:subclass { name = "BossShadow"; preRender = function(self) Shadow.preRender(self) if (self.owner) then self.alpha = self.owner.alpha * 0.5 end end; defaultproperties = { bitmap = m_get_bitmap("boss_shadow.bmp"), draw_mode = DM_MULTIPLY, }; } -- -- BossBlock -- BossBlock = Actor:subclass { name = "BossBlock"; defaultproperties = { w = 5, h = 1, obstacle = 0, -- Not an obstacle permanently, used by scripts }; } --- NEW FILE: CaveLang.lua --- -- -- This object holds written text, to easy translation of the game. -- import("Lang.lua") CaveLang = Lang:subclass { name = "BBRpgLang"; -- -- An array containing speaker names and single words or word combinations. -- vars = { PLAYER = "Frode", CAVEMAN = "Caveman", CAVEMEN = "Cavemen", THE_LORD = "The Lord", HEALTH = "Health", EXPERIENCE = "Experience", STRENGTH = "Strength", DEXTERITY = "Dexterity", AGILITY = "Agility", ENDURANCE = "Endurance", }; -- -- An array containing all the conversations present in the game -- convs = { -- Game just starting INTRO = { {"{PLAYER}", "Arghh... My head hurts like hell. What happened?"}, {"{PLAYER}", "Alright, if you've read the README.TXT then by now you'll know how I got stranded in this cave. Further more, you'll be informed about which keys to use to control me. Well now, let's get some exploration done."}, }, -- Pickaxe and pile of rubble PILE_WONT_BUDGE = { {"{PLAYER}", "It won't budge. I need some tool to get through this..."}, }, PICKUP_PICKAXE = { {"{PLAYER}", "This should come in handy for large stony obstacles!"}, }, USE_PICKAXE = { {"{PLAYER}", "Now let's see how this pickaxe works..."}, }, PICKAXE_WORKED = { {"{PLAYER}", "Pfew. That seems to have worked just fine. Pity my pickace was destroyed in the process."}, }, -- Player goes into prison YAWN_TIRED = { {"{PLAYER}", "<Yawn> I'm tired... I really have to sit down for a while."}, }, QUIET_CORNER = { {"{PLAYER}", "This seems like a quiet corner."}, }, REALLY_TIRED = { {"{PLAYER}", "Djeezz... I'm really... tired..."}, }, DISCOVER_HUMAN = { {"{CAVEMAN}", "What the hack? A human?! I must inform the king immediately."}, }, INFORM_KING = { {"{CAVEMAN}", "Lord, do I have permission to speak?"}, {"{THE_LORD}", "What is it, you lowly caveman slave of mine?"}, {"{CAVEMAN}", "Thank you for letting me speak Lord. I found a human asleep to the south of here."}, {"{THE_LORD}", "Well, what have you done with him?"}, {"{CAVEMAN}", "Nothing, I thought I should tell you about it first..."}, {"{THE_LORD}", "No, you shouldn't have told me first you imbecile! Bring him to me immediately!"}, {"{CAVEMAN}", "Yes Lord, of course."}, }, KICK_FIRST = { {"{CAVEMAN}", "Wait! Let's make sure he doesn't wake up too soon."}, }, BRING_TO_KING = { {"{CAVEMAN}", "Now let's bring him to the king."}, }, BROUGHT_HUMAN = { {"{CAVEMAN}", "Lord, we've brought you the human."}, {"{THE_LORD}", "I can see that you moron."}, {"{CAVEMAN}", "What do you want us to do with him?"}, {"{THE_LORD}", "Hmmm... I'd like to eat him with breakfast. Until then, throw him into prison."}, {"{CAVEMAN}", "Yes Lord, of course."}, }, WHAT_THE = { {"{PLAYER}", "What the..."}, }, WHAT_HAPPENED = { {"{PLAYER}", "What happened? I'm in prison? I must have fallen asleep. Damn!"}, }, -- Player escapes from prison YEAH_RIGHT = { {"{PLAYER}", "Yeah, right. Like they'd forget to lock a prison door."}, }, NOT_LOCKED = { {"{PLAYER}", "Huh? It's not locked! These guys are really stupid!"}, }, WOW_ESCAPED = { {"{CAVEMAN}", "Wow, you escaped!"}, }, MUST_BE_STRONG = { {"{CAVEMAN}", "You must be very strong!"}, }, KING_IS_SLAVEDRIVER = { {"{PLAYER}", "Well, I guess there is no point in denying that."}, {"{CAVEMAN}", "You know, that big guy who commanded us to throw you into prison, he's a slavedriver to us!"}, {"{PLAYER}", "Ah."}, {"{CAVEMAN}", "You might be strong enough to face him in a fight, it would free us from his evil might!"}, {"{PLAYER}", "Hmm. I'd like to ask him to go first, and just see what happens."}, {"{CAVEMAN}", "Cool, we will be forever grateful!! The big bully is to the west, and then up north."}, }, -- Player reaches boss BOSS_AFTER_PRISON = { {"{PLAYER}", "Hello Lord of the Cavemen."}, {"{THE_LORD}", "WHAT? How did you escape from my prison!?"}, {"{PLAYER}", "I walked out really, though I did meet some resistance. But never mind that, I want to talk to you about something."}, {"{THE_LORD}", "If you must. But I'm not the talkative type, so make it quick."}, {"{PLAYER}", "It has come to my knowledge that you are an evil slavedriver. Would you be so kind to step off the throne and let these Cavemen free?"}, {"{THE_LORD}", "Over my fat body!"}, {"{PLAYER}", "I suppose that means no."}, {"{THE_LORD}", "Indeed! Now feel my power, I've had enough and you'll pay for your ignorance!"}, }, BOSS_BEATEN_1 = { {"{THE_LORD}", "Blurk! Bwah! Blleeeh! You are too much for me."}, {"{PLAYER}", "Well, I'm glad that's over with."}, {"{THE_LORD}", "Oh no, not so fast buddy. I might let you live for now, but I'll be back! Bigger, Badder and more Brutal than ever before! Muhahahaha!"}, }, BOSS_BEATEN_2 = { {"{PLAYER}", "Well, I'm not falling for such stupid jokes. Let's see what the Cavemen have to say."}, }, BOSS_BEATEN_3 = { {"{CAVEMEN}", "Hurray! Hurray! Hurray!"}, {"{PLAYER}", "Neat."}, }, -- Some tables with random texts. CDs = { {{"Frode", "Let's see if there's another CD of the Village People..."}}, }, Radio = { {{"Frode", "Cool music."}}, {{"Frode", "I like the Beach Boys's music."}}, {{"Frode", "Yeah! Great soundsystem!"}}, }, Refreshing = { {{"{PLAYER}", "Ah, refreshing."}}, {{"{PLAYER}", "I could use some of that."}}, {{"{PLAYER}", "That's much better."}}, }, Fire = { {{"{PLAYER}", "It's burning just fine."}}, {{"{PLAYER}", "It's too big to carry along. Besides, I'd only use it for evil."}}, {{"{PLAYER}", "Ouch! Fire hot."}}, {{"{PLAYER}", "Is this a logfire I see before me?"}}, }, Escape = { {{"{PLAYER}", "No way I can get out of this cave alive through this!"}}, {{"{PLAYER}", "Escaping through this would kill me for sure."}}, }, }; } --- NEW FILE: CaveObjects.lua --- -- -- Cave-specific objects -- import("Decoration.lua") CaveWaterfallExit = Decoration:subclass { name = "CaveWaterfallExit"; defaultproperties = { bCenterOnTile = false, animType = LinearAnimation, animSeq = extr_array(m_get_bitmap("cave_waterfall_exit.bmp"), 72, 48), animSpeed = 1 / 10, convTableKeyword = "Escape", }; } -- -- Top, Roof and Bottom, used for caves you can walk through -- CaveTunnelTop = Decoration:subclass { name = "CaveTunnelTop"; defaultproperties = { draw_mode = DM_ALPHA, bitmap = m_get_bitmap("cave_entrance2.tga"), offset_z = 48, offset_y = 24, obstacle = 0, }; } CaveRoof = Decoration:subclass { name = "CaveRoof"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("cave_roof.bmp"), offset_z = 48, offset_y = 48, obstacle = 0, }; } CaveTunnelBottom = Decoration:subclass { name = "CaveTunnelBottom"; defaultproperties = { draw_mode = DM_ALPHA, bitmap = m_get_bitmap("cave_entrance.tga"), offset_z = 48, offset_y = 28, obstacle = 0, }; } -- Spider web SpiderWeb = Decoration:subclass { name = "SpiderWeb"; defaultproperties = { draw_mode = DM_ALPHA, bitmap = m_get_bitmap("web.tga"), offset_y = 7, offset_x = 10, }; } SpiderWeb2 = Decoration:subclass { name = "SpiderWeb2"; defaultproperties = { draw_mode = DM_ALPHA, bitmap = m_get_bitmap("web2.tga"), offset_y = 7, offset_x = -10, } } -- -- The horizontal bridge elements -- CaveBridge1 = Decoration:subclass { name = "CaveBridge1"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("cavebridge1.bmp"), offset_x = 0, offset_y = -10, offset_z = -10, obstacle = 0, }; } CaveBridge2 = Decoration:subclass { name = "CaveBridge2"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("cavebridge2.bmp"), offset_x = 0, offset_y = 0, offset_z = 0, obstacle = 0, }; } -- Pile of rubble CavePileTop = Decoration:subclass { name = "CavePileTop"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("rubble_t.bmp"), offset_x = 24, offset_y = 5, offset_z = 0, obstacle = 1, w = 3, h = 2, }; } CavePileBottom = Decoration:subclass { name = "CavePileBottom"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("rubble_b.bmp"), offset_x = 24, offset_y = -10, offset_z = 0, obstacle = 1, w = 3, h = 2, }; } CavePile = Decoration:subclass { name = "CavePile"; activatedBy = function(self, instigator) if (picked_up_pick) then ActionController:addSequence({ ActionExModeOn(), ActionConversation(lang:getConv("USE_PICKAXE")), ActionFadeOutMap(100), ActionDestroyObject(self), ActionAddObject(CavePileTop, 16, 29), ActionAddObject(CavePileBottom, 16, 32), ActionFadeInMap(100), ActionConversation(lang:getConv("PICKAXE_WORKED")), ActionExModeOff(), }) else ActionController:addSequence({ ActionConversation(lang:getConv("PILE_WONT_BUDGE")), }) end end; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("cavepile.bmp"), offset_x = 24, offset_y = -12, offset_z = 0, obstacle = 1, w = 3, h = 5, bCanActivate = true, }; } -- Pick and howel CavePick = Decoration:subclass { name = "CavePick"; activatedBy = function(self, obj) picked_up_pick = 1 m_destroy_object(self) ActionController:addSequence({ ActionConversation(lang:getConv("PICKUP_PICKAXE")) }) end; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("cavepick.bmp"), obstacle = 1, offset_x = 0, offset_y = -10, offset_z = -10, bCanActivate = true, }; } --- NEW FILE: CaveTriggers.lua --- -- cave_triggers.lua -- Cave-specified triggers -- By Georg Muntingh CaveFallingAsleep = Actor:subclass { name = "CaveFallingAsleep"; event_stand_on = function(self, instigator) local player = m_get_player() if (instigator ~= m_get_player()) then -- Some monster that wandered too far away is moving into this critical area, -- so get rid of him. instigator:take_damage(100) return end ActionController:addSequence({ -- The player gets tired and is going to get some rest in a quiet corner. ActionConversation(lang:getConv("YAWN_TIRED")), ActionExModeOn(), ActionWalk(player, DIR_LEFT, 7), ActionChangeDirection(player, DIR_DOWN), ActionWait(25), ActionConversation(lang:getConv("QUIET_CORNER")), ActionWait(25), ActionSetVariable(m_get_player(), "tick_time", 0), -- DISABLELNG PLAYER TICK! ActionChangeBitmap(m_get_player(), m_get_bitmap("frode_sit1.tga")), ActionWait(25), ActionConversation(lang:getConv("REALLY_TIRED")), ActionWait(25), ActionChangeBitmap(m_get_player(), m_get_bitmap("frode_sit2.tga")), ActionWait(100), -- The player has fallen asleep, time passes... ActionFadeOutMap(50), ActionWait(50), ActionFadeInMap(50), -- Suddenly, a caveman arrives and notices the player. He runs away, to warn -- his leader as soon as possible. ActionWalk(caveman1, DIR_DOWN, 5), ActionChangeDirection(caveman1, DIR_LEFT), ActionWait(50), ActionSetVariable(caveman1, "speed", 5.4), ActionWalk(caveman1, DIR_LEFT, 3), ActionWait(50), ActionConversation(lang:getConv("DISCOVER_HUMAN")), ActionAddSequence({ ActionWalk(caveman1, DIR_RIGHT, 3), }), ActionFadeOutMap(56), -- The caveman arrives at his leader. His leader sends him back to fetch the -- human straight away. ActionChangeMap("data/maps/cave3.map"), ActionSetVariable(camera, "target", caveman1), ActionSetVariable(caveman1, "offset_x", 0), ActionSetPosition(caveman1, 35, 16, DIR_UP), ActionAddSequence({ ActionFadeInMap(50), }), ActionWalk(caveman1, DIR_UP, 3), ActionConversation(lang:getConv("INFORM_KING")), ActionAddSequence({ ActionFadeOutMap(56), }), ActionWalk(caveman1, DIR_DOWN, 3), -- Two cavemen arrive at the player, cast a spell on him and take him back -- to the king ActionChangeMap("data/maps/cave2.map"), ActionSetPosition(caveman1, 24, 4, DIR_DOWN), ActionSetPosition(caveman2, 24, 3, DIR_DOWN), ActionSetVariable(caveman2, "speed", 5.4), ActionAddSequence({ ActionAddSequence({ ActionFadeInMap(50), }), ActionWalk(caveman1, DIR_DOWN, 5), ActionWalk(caveman1, DIR_LEFT, 5), }), ActionWalk(caveman2, DIR_DOWN, 6), ActionWalk(caveman2, DIR_LEFT, 4), ActionWait(50), ActionConversation(lang:getConv("KICK_FIRST")), ActionWait(50), ActionSetVariable(caveman1,"attacking", 1), ActionCallFunction(SpawnSparkyHit, 18, 10, player.offset_x, player.offset_y, player.offset_z + 24), ActionWait(40), ActionSetVariable(caveman1,"attacking", 0), ActionWait(10), ActionConversation(lang:getConv("BRING_TO_KING")), ActionFadeOutMap(50), -- The two cavemen and the player arrive at the king. The king tells the -- cavemen he wants the human in his prison. ActionSetPosition(camera_handle, 35, 12), ActionSetVariable(camera, "target", camera_handle), ActionChangeMap("data/maps/cave3.map"), ActionSetPosition(caveman1, 34, 13, DIR_UP), ActionSetPosition(m_get_player(), 35, 13), ActionSetPosition(caveman2, 36, 13, DIR_UP), ActionChangeBitmap(m_get_player(), m_get_bitmap("frode_sit2.tga")), ActionFadeInMap(50), ActionConversation(lang:getConv("BROUGHT_HUMAN")), ActionFadeOutMap(50), -- The player finds himself locked in a prison. ActionWait(50), ActionSetVariable(camera, "target", m_get_player()), ActionSetPosition(m_get_player(), 80, 20), ActionFadeInMap(50), ActionWait(50), ActionChangeBitmap(m_get_player(), m_get_bitmap("frode_sit1.tga")), ActionWait(50), ActionSetVariable(m_get_player(), "tick_time", 1), -- ENABLEING PLAYER TICK! ActionChangeDirection(m_get_player(), DIR_DOWN), ActionConversation(lang:getConv("WHAT_THE")), ActionWalk(m_get_player(), DIR_RIGHT, 2), ActionWalk(m_get_player(), DIR_DOWN, 4), ActionConversation(lang:getConv("WHAT_HAPPENED")), -- Prepare caveman for next sequence (when escaping from prison) ActionSetPosition(caveman1, 67, 34, DIR_DOWN), ActionSetPosition(caveman2, 69, 34, DIR_DOWN), ActionExModeOff(), ActionShowMapName(m_get_bitmap("cave_title_3.bmp")), }) end; } CaveNoticeStrong = Actor:subclass { name = "CaveNoticeStron"; event_stand_on = function(self, instigator) if (instigator ~= m_get_player()) then instigator:take_damage(100) return end ActionController:addSequence({ -- The player arrives at two cavemen guards. They are impressed by -- his escape and tell him the story about their slavedriver, asking -- the player to kill him. ActionExModeOn(), ActionDestroyObject(self), ActionWait(50), ActionChangeDirection(caveman1, DIR_RIGHT), ActionChangeDirection(caveman2, DIR_LEFT), ActionWait(50), ActionChangeDirection(m_get_player(), DIR_LEFT), ActionConversation(lang:getConv("WOW_ESCAPED")), ActionChangeDirection(m_get_player(), DIR_RIGHT), ActionConversation(lang:getConv("MUST_BE_STRONG")), ActionWait(50), ActionChangeDirection(m_get_player(), DIR_DOWN), ActionWait(100), ActionConversation(lang:getConv("KING_IS_SLAVEDRIVER")), ActionChangeDirection(caveman1, DIR_DOWN), ActionChangeDirection(caveman2, DIR_DOWN), ActionExModeOff(), }) end; } --- NEW FILE: Caveman.lua --- -- -- Caveman classes -- By Bjorn Lindeijer import("Enemy.lua") import("AnimationFunctions.lua") Caveman = Character:subclass { name = "Caveman"; defaultproperties = { draw_mode = DM_MASKED, speed = 2, charAnim = extr_char_anim(m_get_bitmap("caveman.bmp")), }; } -- -- The caveman enemy object -- EnemyCaveman = Enemy:subclass { name = "EnemyCaveman"; setState = function(self, state) self.state = state if (self.state == AI_ATTACK) then self.attacking = 1 else self.attacking = 0 end self:updateBitmap() end; died = function(self, killer) Enemy.died(self, killer) self.offset_y = self.offset_y + 3 end; attack = function(self, obj) if (obj) then SpawnSparkyHit(obj.x, obj.y, obj.offset_x, obj.offset_y, obj.offset_z + 24) end end; defaultproperties = { experience = 17, draw_mode = DM_MASKED, speed = 2, charAnim = extr_char_anim(m_get_bitmap("caveman.bmp")), deathBitmap = m_get_bitmap("caveman_dead.bmp"), }; } -- -- The caveman slave object -- CavemanSlave = Character:subclass { name = "CavemanSlave"; defaultproperties = { draw_mode = DM_MASKED, speed = 2, charAnim = extr_char_anim(m_get_bitmap("caveman_slave.bmp")), }; } -- -- A dead slave -- SlaveDead = Character:subclass { name = "SlaveDead"; defaultproperties = { draw_mode = DM_MASKED, bitmap = m_get_bitmap("slave_dead.bmp"), }; } --- NEW FILE: ConversationWindow.lua --- -- -- This file contains the conversation windows class. -- -- By Bjorn Lindeijer import("Interaction.lua") -- Receives a single string and returns array of tokens. -- Ex: "Hello world!" -> {"Hello", "world!"} tokenize = function(str) local tokens = {} local str_len = string.len(str) local start = 1 while (start < str_len) do from, to, token = string.find(str, "%s?([^%s]*)", start) start = to + 1 table.insert(tokens, token) end return tokens end detokenize = function(tokens) token_cnt = table.getn(tokens) if (token_cnt > 0) then local string = tokens[1] local n = 2 while (n <= token_cnt) do string = string.." "..tokens[n] n = n + 1 end return string else return "" end end CB_CLOSED = 0 CB_READY = 1 CB_WRITING = 2 CB_SCROLLING = 3 CB_WAITING = 4 CB_SCALING = 5 ConversationWindow = Interaction:subclass { name = "ConversationWindow"; init = function(self) -- Get and set some sizes self.font = "font_sansserif8.pcx" m_set_font(self.font) self.space_width, self.line_height = m_text_size(" ") local w, h = m_screen_size() self.x = w / 4 self.y = h - h / 4 self.w = w / 2 self.h = h / 2 - h / 3 + 5 - 2 self.nr_lines = math.floor(self.h / self.line_height) end; write_line = function(self, string) table.insert(self.lines_todo, string) if (self.state == CB_CLOSED) then self.state = CB_SCALING ActionController:addSequence{ ActionExModeOn(), ActionTweenVariable(self, "width", self.appear_time, 1), ActionTweenVariable(self, "height", self.appear_time, 1), ActionSetVariable(self, "state", CB_READY), } -- grab control over the player --m_set_ex_mode(1) --self.state = CB_READY end end; tick = function(self) if (self.state == CB_CLOSED) then return elseif (self.state == CB_READY and table.getn(self.lines_todo) > 0) then if (table.getn(self.lines) < self.nr_lines) then -- There is space, start writing the line. -- We need the correct font set when calculating how big the text is m_set_font(self.font) local tokens = tokenize(self.lines_todo[1]) local new_line = tokens[1] table.remove(tokens, 1) while (table.getn(tokens) > 0 and m_text_size(new_line.." "..tokens[1]) < self.w) do new_line = new_line.." "..tokens[1] table.remove(tokens, 1) end -- Continue indicates if writing should continue after this line -- or if it should show a blinking square (end of sentence) if (table.getn(tokens) > 0) then self.continue = 1 self.lines_todo[1] = detokenize(tokens) else self.continue = nil table.remove(self.lines_todo, 1) end table.insert(self.lines, new_line) self.curr_char = 1 self.state = CB_WRITING else -- No line left, scroll up two lines. if (self.continue) then self:set_state(CB_WAITING) else self.state = CB_SCROLLING end end elseif (self.state == CB_READY) then -- Ready and no lines left todo ActionController:addSequence{ ActionSetVariable(self, "state", CB_SCALING), ActionTweenVariable(self, "height", self.appear_time, 0), ActionTweenVariable(self, "width", self.appear_time, 0), ActionSetVariable(self, "state", CB_CLOSED), ActionExModeOff(), } --self.state = CB_CLOSED -- return control to the player --m_set_ex_mode(0) elseif (self.state == CB_SCROLLING) then if (self.scroll < (self.nr_lines - 1) * self.line_height) then self.scroll = self.scroll + 1 else for n = 1,(self.nr_lines - 1) do table.remove(self.lines, 1) end self.scroll = 0 self.state = CB_READY end elseif (self.state == CB_WRITING) then local current_string = self.lines[table.getn(self.lines)] local length = string.len(current_string) if (self.curr_char < length) then self.curr_char = self.curr_char + 0.5 else -- This line has finished, either wait for key or continue -- with next line. if (self.continue) then self.state = CB_READY else self:set_state(CB_WAITING) end end elseif (self.state == CB_WAITING) then if (self.blink == 1) then -- Set appropriate blinking bitmap if (self.blink_bitmap) then self.blink_bitmap = nil else if (self.continue) then self.blink_bitmap = m_get_bitmap("arrow.bmp") else self.blink_bitmap = m_get_bitmap("square.bmp") end end end self.blink = self.blink + 1 if (self.blink > 20) then self.blink = 1 end end end; keyType = function(self, key) if (key == "action" and self.state == CB_WAITING) then if (self.continue) then -- Scroll up self.state = CB_SCROLLING else self.state = CB_READY end return true end return false end; postRender = function(self, canvas) if (self.state == CB_CLOSED) then return end local screen_w, screen_h = m_screen_size() m_set_font(self.font) if (self.state ~= CB_SCALING) then -- Draw the shadow of the text m_set_clip(self.x, self.y, self.x + self.w - 1, self.y + self.h - 1) for n = 1, table.getn(self.lines) do m_set_color(0, 0, 0) m_set_cursor(self.x + 1, self.y + (n - 1) * self.line_height - self.scroll + 1) if (n == table.getn(self.lines)) then m_draw_text(string.sub(self.lines[n], 1, self.curr_char)) else m_draw_text(self.lines[n]) end end end -- Draw the box m_set_clip(0, 0, screen_w - 1, screen_h - 1) guiTheme:drawBox( (self.x + self.w / 2) - (self.w * self.width) / 2 - 4, (self.y + self.h / 2) - (self.h * self.height) / 2 - 4, self.w * self.width + 8, self.h * self.height + 8 ) if (self.state ~= CB_SCALING) then -- Draw the blinking icon if (self.state == CB_WAITING and self.blink_bitmap) then local w, h = m_bitmap_size(self.blink_bitmap) m_set_cursor(self.x + self.w - w, self.y + self.h - h) canvas:drawIcon(self.blink_bitmap, 1) end -- Draw the lines of text m_set_clip(self.x, self.y, self.x + self.w - 1, self.y + self.h - 1) for n = 1, table.getn(self.lines) do m_set_color(guiTheme:getTextColor()) m_set_cursor(self.x, self.y + (n - 1) * self.line_height - self.scroll) if (n == table.getn(self.lines)) then m_draw_text(string.sub(self.lines[n], 1, self.curr_char)) else m_draw_text(self.lines[n]) end end end local w, h = m_screen_size() m_set_clip(0, 0, w - 1, h - 1) end; set_state = function(self, state) self.state = state if (self.state == CB_WAITING) then self.blink = 2 if (self.continue) then self.blink_bitmap = m_get_bitmap("arrow.bmp") else self.blink_bitmap = m_get_bitmap("square.bmp") end end end; defaultproperties = { bRequiresTick = true, lines = {}, lines_todo = {}, state = CB_CLOSED, scroll = 0, curr_char = 1, continue = nil, blink = 1, width = 0, height = 0, appear_time = 15, }; } --- NEW FILE: DgeMapSwitches.lua --- -- -- Here we define some player sequences. -- By Georg Muntingh Buiten_Naar_Bos = {} function Buiten_Naar_Bos:event_stand_on(obj) local player = m_get_player() if obj ~= player or m_get_ex_mode() == 1 then return end ActionController:addSequence({ ActionExModeOn(), ActionWalk(player, DIR_RIGHT, 2), ActionChangeMap("data/maps/bos.map"), ActionSetPosition(player, -1, player.y + 6, DIR_RIGHT), ActionAddSequence({ ActionWalk(player, DIR_RIGHT, 3), ActionExModeOff(), }), ActionFadeInMusic("data/music/KR-UT2003-Menu.ogg", 50, 0), ActionFadeInMap(50), }) ActionController:addSequence({ ActionFadeOutMusic(50, 0), ActionFadeOutMap(50), }) end Bos_Naar_Buiten = {} function Bos_Naar_Buiten:event_stand_on(obj) local player = m_get_player() if obj ~= player or m_get_ex_mode() == 1 then return end ActionController:addSequence({ ActionExModeOn(), ActionWalk(player, DIR_LEFT, 2), ActionChangeMap("data/maps/buiten.map"), ActionSetPosition(player, 85, player.y - 6, DIR_LEFT), ActionAddSequence({ ActionWalk(player, DIR_LEFT, 3), ActionExModeOff(), }), ActionFadeInMusic("data/music/KR-UT2003-Menu.ogg", 50, 0), ActionFadeInMap(50), }) ActionController:addSequence({ ActionFadeOutMusic(50, 0), ActionFadeOutMap(50), }) end Buiten_Naar_Tent1 = {} function Buiten_Naar_Tent1:activatedBy(instigator) local player = m_get_player() if instigator ~= player or player.dir ~= DIR_UP then return end local text_table = { {{"Frode", "A small tent for Georg and Laurens"}}, {{"Frode", "It's a mess inside."}}, } repeat n = math.random(table.getn(text_table)) until (n ~= self.prev_random) self.prev_random = n ActionController:addSequence({ ActionFadeOutMap(50), ActionChangeMap("data/maps/tent1.map"), ActionSetPosition(player, 7, 9, DIR_UP), ActionFadeInMap(50), ActionConversation(text_table[n]), }) end Buiten_Naar_Tent2 = {} function Buiten_Naar_Tent2:activatedBy(instigator) local player = m_get_player() if instigator ~= player or player.dir ~= DIR_UP then return end local text_table = { {{"Frode", "Small tent for Chris and Margje"}}, {{"Frode", "It's a terrible mess inside."}}, } repeat n = math.random(table.getn(text_table)) until (n ~= self.prev_random) self.prev_random = n ActionController:addSequence({ ActionFadeOutMap(50), ActionChangeMap("data/maps/tent1.map"), ActionSetPosition(player, 21, 10, DIR_UP), ActionFadeInMap(50), ActionConversation(text_table[n]), }) end Tent1_Naar_Buiten = {} function Tent1_Naar_Buiten:event_stand_on(instigator) local player = m_get_player() if instigator ~= player then return end ActionController:addSequence({ ActionExModeOn(), ActionFadeOutMap(50), ActionChangeMap("data/maps/buiten.map"), ActionSetPosition(player, 28, 21, DIR_DOWN), ActionFadeInMap(50), ActionExModeOff(), }) end Tent2_Naar_Buiten = {} function Tent2_Naar_Buiten:event_stand_on(instigator) local player = m_get_player() if instigator ~= player then return end ActionController:addSequence({ ActionExModeOn(), ActionFadeOutMap(50), ActionChangeMap("data/maps/buiten.map"), ActionSetPosition(player, 22, 21, DIR_DOWN), ActionFadeInMap(50), ActionExModeOff(), }) end Cave1_Naar_Cave2 = Actor:subclass { name = "Cave1_Naar_Cave2"; event_stand_on = function(self, obj) local player = m_get_player() if obj ~= player or m_get_ex_mode() == 1 then return end ActionController:addSequence({ ActionExModeOn(), ActionAddSequence({ ActionWalk(player, DIR_LEFT, 1), }), ActionFadeOutMap(50), ActionChangeMap("data/maps/cave2.map"), ActionShowMapName(m_get_bitmap("cave_title_2.bmp")), ActionSetPosition(player, 64, player.y + 57, DIR_LEFT), ActionAddSequence({ ActionWalk(player, DIR_LEFT, 2), ActionExModeOff(), }), ActionFadeInMap(50), }) end; } Cave2_Naar_Cave1 = Actor:subclass { name = "Cave2_Naar_Cave1"; event_stand_on = function(self, obj) local player = m_get_player() if obj ~= player or m_get_ex_mode() == 1 then return end ActionController:addSequence({ ActionExModeOn(), ActionAddSequence({ ActionWalk(player, DIR_RIGHT, 1), }), ActionFadeOutMap(50), ActionChangeMap("data/maps/cave1.map"), ActionShowMapName(m_get_bitmap("cave_title_1.bmp")), ActionSetPosition(player, -1, player.y - 57, DIR_RIGHT), ActionAddSequence({ ActionWalk(player, DIR_RIGHT, 2), ActionExModeOff(), }), ActionFadeInMap(50), }) end; } --- NEW FILE: Dummy.lua --- import("Actor.lua") Dummy = Actor:subclass { name = "Dummy"; defaultproperties = { bitmap = nil,-- m_get_bitmap("zaklamp.bmp"), }; } --- NEW FILE: Game.lua --- -- -- This file defines some global functions for the game. -- By Bjorn Lindeijer import("Object.lua") import("ConversationWindow.lua") import("GuiTheme.lua") Game = Object:subclass { name = "Game"; init = function(self) -- Initialize stuff MusicControl:init() guiTheme = self.guiThemeClass() -- WARNING: Bad design, introducing a global variable! convBox = self.conversationWindowClass() -- WARNING: I have no shame! if (self.textProviderClass ~= nil) then self.textProvider = self.textProviderClass() end local width, height = m_screen_size() --self.viewPort = Viewport( -- (width - 320) * 0.5, -- (height - 240) * 0.5, -- 320, 240 --) self.viewPort = Viewport(0, 0, width, height) self.canvas = Canvas() self.interactionMaster = InteractionMaster(self.viewPort) interactionMaster = self.interactionMaster -- Make a nice global variable! -- Create HUD if a HUD class was given if (self.hudClass) then self.hud = self.hudClass() self.interactionMaster:addInteraction(self.hud) hud = self.hud -- Yeah, make it global! end -- Add the conversation box self.interactionMaster:addInteraction(convBox) -- Create menu if a menu class was given if (self.mainMenuClass) then self.mainMenu = self.mainMenuClass() self.interactionMaster:addInteraction(self.mainMenu) end end; getText = function(self, id) if (self.textProvider) then return self.textProvider:getText(id, self.lang) end end; -- -- This function is called every game update, right before all objects get -- updated. -- event_logic_update = function(self) ActionController:update() MusicControl:update() self.interactionMaster:processTick() if (playerController and m_get_ex_mode() == 0) then m_update_input(playerController) playerController:playerTick() end end; -- -- This function is called right after the map and objects have been rendered -- by the engine. It is meant to draw stuff on the screen that is not actually -- present on the map, like text boxes and info displays. -- -- Here's a general set of functions that can be used for positioning and -- drawing on the screen. The draw functions should generally only be used -- within this function call. -- -- function m_get_cursor() // returns x, y -- function m_set_cursor(x, y) -- function m_text_size(text) // returns width, height -- function m_bitmap_size(bitmap) // returns width, height -- function m_screen_size() // returns width, height -- function m_draw_text(text) -- function m_draw_bitmap(bitmap, dest_w, dest_h, src_x, src_y, src_w, src_h) -- function m_draw_viewport(x, y, w, h, target) -- event_render = function(self) self.interactionMaster:processPreRender() if (not show_main_menu and self.viewPort) then self.viewPort:render() end -- Set HUD to invisible while main menu is shown if (self.hud) then self.hud.bVisible = not show_main_menu end if (map_fade ~= nil and map_fade.alpha ~= nil and map_fade.alpha > 0) then local w,h = m_screen_size() m_set_alpha(map_fade.alpha) m_set_cursor(0,0) self.canvas:drawRect(m_get_bitmap("pixel_black.bmp"), w, h) m_set_alpha(255) end self.interactionMaster:processPostRender(self.canvas) end; event_keypress = function(self, key) --m_message(key.." key has been pressed!") if (self.interactionMaster:processKeyType(key)) then return true end if (key == "esc") then if (self.ingameMenuClass) then self.interactionMaster:addInteraction(self.ingameMenuClass()) else m_quit_game() end return true end end; defaultproperties = { startupSequence = {}, conversationWindowClass = ConversationWindow, guiThemeClass = GuiTheme, hudClass = nil, playerClass = nil, mainMenuClass = nil, ingameMenuClass = nil, textProviderClass = nil, textProvider = nil, mainMenu = nil, viewPort = nil, lang = "en", } } -- -- Below here some ugly leftovers -- game = { game_over = 0, game_over_alpha = 0, } camera = { target = nil, } -- -- A helper function used by scripts that want to show a conversation. -- function write_conversation(data) local conversation = {} for index, value in pairs(data) do local name = value[1] -- WARNING: Only works for BBRpg! --[[ if (name == "{PLAYER}") then player = playerSwitcher:getCurrentHost() if (player) then name = player.name end end ]] table.insert(conversation, name..": \""..value[2].."\"") end local n = table.getn(conversation) convBox.lines = {} convBox.lines_todo = {} convBox.state = CB_CLOSED convBox.scroll = 0 convBox.curr_char = 1 convBox.continue = nil for i = 1, n do convBox:write_line(conversation[i]) end end -- -- Another helper function to make it easier to choose a random thing to say, unequal -- to what was said last time. -- function get_new_n(old_n, max_n) local n if (max_n > 1) then repeat n = math.random(max_n) until (n ~= old_n) else n = max_n end return n end --- NEW FILE: Guard.lua --- import("Character.lua") Guard = Character:subclass { name = "Guard"; defaultproperties = { draw_mode = DM_MASKED, charAnim = extr_char_anim(m_get_bitmap("cop.bmp"), 23, 40), dir = DIR_UP, speed = 3, }; } --- NEW FILE: IngameMenu.lua --- -- -- The ingame menu -- By Bjorn Lindeijer import("GuiMenu.lua") IngameMenu = GuiMenu:subclass { name = "IngameMenu"; init = function(self) GuiMenu.init(self) self:addMenuItem(GuiMenuItem(lang:getVar("CONTINUE"), function() self.master:removeInteraction(self); end)) self:addMenuItem(GuiMenuItem(lang:getVar("LANGUAGE"), function() self.master:addInteraction(LanguageMenu(self)); self.master:removeInteraction(self); end)) self:addMenuItem(GuiMenuItem(lang:getVar("QUIT"), function() self.master:removeInteraction(self); m_quit_game() end)) end; keyType = function(self, key) if (GuiMenu.keyType(self, key)) then return true end if (key == "esc") then self.master:removeInteraction(self) return true end end; defaultproperties = { } } --- NEW FILE: Interaction.lua --- -- -- Interactions are the foundation for any subsystem that requires interaction -- with the player (such as a menu). One InteractionMaster is used to handle -- all the interactions. -- -- By Bjorn Lindeijer import("Object.lua") Interaction = Object:subclass { name = "Interaction"; -- Tick function, disabled by default but can be activated by setting -- bRequiresTick to true. tick = function(self) end; -- Receives a key description string, should return true when key was -- used by this interaction. keyType = function(self, key) return false end; -- A notification that the current level has changed together with the -- name of the level (the map path, but this should change) levelChange = function(self, level) end; -- A chance to change something just before rendering takes place (requires bVisible) preRender = function(self) end; -- A chance to put something on the screen (requires bVisible) postRender = function(self, canvas) end; -- Sets the focus on this interaction setFocus = function(self) self.master.setFocusTo(self, self.viewportOwner); end; -- Changes the interactionmaster setInteractionMaster = function(self, master) self.master = master end; defaultproperties = { bActive = true, -- Is this interaction Getting Input bVisible = true, -- Is this interaction being Displayed bRequiresTick = false, -- Does this interaction require game Tick master = nil, -- The interaction master viewportOwner = nil, -- Owner of the viewport, ie. the player }; } --- NEW FILE: InteractionMaster.lua --- -- -- The InteractionMaster controls the entire interaction system. It's -- job is to take input, postRender and tick calls and route them to -- individual interactions. -- -- By Bjorn Lindeijer import("Object.lua") InteractionMaster = Object:subclass { name = "InteractionMaster"; init = function(self, viewport) self.viewport = viewport end; addInteraction = function(self, interaction) table.insert(self.globalInteractions, 1, interaction) interaction:setInteractionMaster(self) end; removeInteraction = function(self, interaction) local interactionArray local iIndex interactionArray = self.globalInteractions -- Search for the Interaction iIndex = -1; for i = 1, table.getn(interactionArray) do if (interactionArray[i] == interaction) then iIndex = i end end -- Whas it found? if (iIndex < 0) then m_message("Attempt to remove non-existing interaction.") else interactionArray[iIndex]:setInteractionMaster(nil) table.remove(interactionArray, iIndex) end end; setFocusTo = function(self, interaction, viewportOwner) local interactionArray local temp local i, iIndex --if (ViewportOwner ~= nil) then -- InteractionArray = ViewportOwner.LocalInteractions --else interactionArray = self.globalInteractions --end if (table.getn(interactionArray) == 0) then m_message("Attempt to setFocusTo with an empty interactions array."); return end -- Search for the Interaction iIndex = -1; for i = 1, table.getn(interactionArray) do if (interactionArray[i] == interaction) then iIndex = i end end -- Was it found? if (iIndex < 0) then m_message("Attempt to set focus to a non-existing interaction ("..interaction..").") return elseif (iIndex == 1) then return -- Already has focus end -- Move it to the top. temp = interactionArray[iIndex] for i = iIndex, 2, -1 do interactionArray[i] = interactionArray[i-1] end interactionArray[1] = temp interactionArray[1].bActive = true -- Give it Input interactionArray[1].bVisible = true -- Make it visible end; -- -- The process functions take the events to the interactions. -- processTick = function(self) local ia = self.globalInteractions for i = 1, table.getn(ia) do if (ia[i].bRequiresTick) then ia[i]:tick() end end end; processPreRender = function(self) local ia = self.globalInteractions for i = 1, table.getn(ia) do if (ia[i].bVisible) then ia[i]:preRender() end end end; processPostRender = function(self, canvas) local ia = self.globalInteractions for i = table.getn(ia), 1, -1 do if (ia[i].bVisible) then ia[i]:postRender(canvas) end end end; processKeyType = function(self, key) local ia = self.globalInteractions for i = 1, table.getn(ia) do if (ia[i].bActive) then if (ia[i]:keyType(key)) then return true -- No further processing when key is used end end end return false end; processLevelChange = function(self, level) local ia = self.globalInteractions for i = 1, table.getn(ia) do ia[i]:levelChange(level) end end; defaultproperties = { globalInteractions = {}, viewport = nil, }; } --- NEW FILE: LanguageMenu.lua --- -- A menu to choose your language LanguageMenu = GuiMenu:subclass { name = "LanguageMenu"; init = function(self, parentMenu) GuiMenu.init(self) self.parentMenu = parentMenu self:addMenuItem(GuiMenuItem(BBRpgLang.defaultproperties.languageName, function() lang = BBRpgLang() self.master:addInteraction(_G[parentMenu.name]()) self.master:removeInteraction(self) end)) self:addMenuItem(GuiMenuItem(BBRpgLangDutch.defaultproperties.languageName, function() lang = BBRpgLangDutch() self.master:addInteraction(_G[parentMenu.name]()) self.master:removeInteraction(self) end)) end; keyType = function(self, key) if (GuiMenu.keyType(self, key)) then return true end if (key == "esc") then -- Return without switching language self.master:addInteraction(self.parentMenu) self.master:removeInteraction(self) return true end end; defaultproperties = { }; } --- NEW FILE: MainMenu.lua --- -- -- Het hoofdmenu -- By Bjorn Lindeijer import("GuiMenu.lua") MainMenu = GuiMenu:subclass { name = "MainMenu"; init = function(self) GuiMenu.init(self) local oldspeed = elwood.speed local dummy = cityMap:spawn(Dummy, 113.5, 109.5) local quickStartSequence = { ActionFadeOutMusic(50), ActionFadeOutMap(50), ActionWait(50), ActionSetVariable(_G, "show_main_menu", nil), ActionCallFunction(jake.addToInventory, jake, cityMap.walkieTalkie), ActionCallFunction(elwood.addToInventory, elwood, cityMap.walkieTalkie), ActionSetPosition(elwood, 94, 73, DIR_UP, cityMap), ActionSetPosition(jake, 93, 73, DIR_UP, cityMap), ActionSetCameraTarget(elwood, false), ActionPlaySong(cityMap.musicFilename, 100), ActionFadeInMap(100), ActionExModeOff(), } local startSequence = { ActionFadeOutMusic(50), ActionFadeOutMap(50), ActionWait(50), ActionCallFunction(jake.addToInventory, jake, cityMap.walkieTalkie), ActionSetVariable(_G, "show_main_menu", nil), ActionPlaySong("data/music/2.ogg", 100), ActionFadeInMap(100), ActionShowMapName(m_get_bitmap("prison.tga")), ActionWait(300), ActionConversation(lang:getConv("Intro1")), ActionWait(25), ActionWalkPath(jailMap.guard, "DLD"), ActionSetVariable(elwood, "dir", DIR_DOWN), ActionWait(25), ActionSetVariable(jailMap.guard, "dir", DIR_UP), ActionConversation(lang:getConv("Intro2")), ActionWalkPath(jailMap.guard, "D2"), ActionCallFunction(door1.event_bumped_into, door1), ActionWait(door1.period * 100), ActionWalkPath(elwood, "D"), ActionAddSequence{ ActionSetVariable(elwood, "speed", jailMap.guard.speed), ActionWalkPath(elwood, "D6R9D13L19D14R9"), ActionSetVariable(elwood, "speed", oldspeed), }, ActionWalkPath(jailMap.guard,"D3R9D13L19D14R11"), ActionSetVariable(jailMap.guard, "dir", DIR_LEFT), ActionWait(50), ActionConversation(lang:getConv("Intro3")), ActionWalkPath(elwood, "D5"), ActionAddSequence{ ActionFadeOutMap(50), }, ActionWalkPath(elwood, "D3"), ActionCallFunction(elwood.setMap, elwood, cityMap), ActionSetPosition(elwood, 113, 107, DIR_DOWN), ActionAddSequence{ ActionFadeInMap(50), }, ActionWalkPath(elwood, "D3"), ActionConversation(lang:getConv("Intro4")), ActionSetCameraTarget(dummy, false), ActionTweenVariable(dummy, "y", 200, 114.5), ActionWait(50), ActionWalkPath(elwood, "D5"), ActionSetCameraTarget(elwood, false), ActionWalkPath(elwood, "D3"), ActionSetVariable(jake, "dir", DIR_RIGHT), ActionWalkPath(elwood, "D"), ActionSetVariable(elwood, "dir", DIR_LEFT), ActionConversation(lang:getConv("Intro5")), ActionAddSequence{ ActionWalkPath(elwood, "D5L5"), ActionSetVariable(elwood, "dir", DIR_RIGHT), ActionWait(15), ActionSetPosition(dummy, 108.5, 123), ActionSetCameraTarget(dummy, false), ActionSetPosition(elwood, 113, 108, DIR_DOWN), }, ActionWalkPath(jake, "D3L3"), ActionSetVariable(jake, "dir", DIR_RIGHT), ActionWait(15), ActionSetPosition(jake, 114, 108, DIR_DOWN), ActionWait(180), ActionConversation(lang:getConv("Intro5a")), ActionCallFunction(elwood.addToInventory, elwood, cityMap.walkieTalkie), ActionConversation(lang:getConv("Intro6")), ActionSetVariable(copcar, "tick_time", 5), ActionTweenVariable(copcar, "x", 250, 118, function(from, to, perc) perc = 1 - math.sin(perc * 0.5 * math.pi + 0.5 * math.pi) return from + (to - from) * perc end), ActionFadeOutMap(50), ActionSetPosition(dummy, 90.5, 75.5), ActionFadeInMap(50), ActionShowMapName(m_get_bitmap("suburbs.tga")), ActionSetPosition(copcar, 76, 77), ActionTweenVariable(copcar, "x", 250, 88, function(from, to, perc) perc = math.sin(perc * 0.5 * math.pi) return from + (to - from) * perc end), ActionSetVariable(copcar, "tick_time", 0), ActionWait(50), ActionSetPosition(elwood, 90, 78, DIR_DOWN), ActionWait(10), ActionSetPosition(elwood, 90, 78, DIR_RIGHT), ActionWait(30), ActionSetPosition(jake, 90, 76, DIR_UP), ActionWait(10), ActionSetPosition(jake, 90, 76, DIR_RIGHT), ActionConversation(lang:getConv("Intro7")), ActionSetCameraTarget(jake, false), ActionParallel{ ActionSequence{ ActionWait(30), ActionWalkPath(jake, "R3U3"), }, ActionWalkPath(elwood, "R4U5"), }, ActionWait(150), ActionSetVariable(jake, "dir", DIR_RIGHT), ActionWait(30), ActionSetVariable(elwood, "dir", DIR_LEFT), ActionConversation(lang:getConv("WhereKeys")), ActionSetPosition(dummy, 93.5, 72.5), ActionSetCameraTarget(dummy, false), ActionFadeOutMusic(50), ActionTweenVariable(dummy, "x", 50, 94.5), ActionPlaySong(cityMap.musicFilename, 10), ActionSetCameraTarget(elwood, false), ActionExModeOff(), } self:addMenuItem(GuiMenuItem(lang:getVar("PLAY"), function() self.master:removeInteraction(self); ActionController:addSequence(startSequence); end)) self:addMenuItem(GuiMenuItem(lang:getVar("QUICKPLAY"), function() self.master:removeInteraction(self); ActionController:addSequence(quickStartSequence); end)) self:addMenuItem(GuiMenuItem(lang:getVar("LANGUAGE"), function() self.master:addInteraction(LanguageMenu(self)); self.master:removeInteraction(self); end)) self:addMenuItem(GuiMenuItem(lang:getVar("QUIT"), function() self.master:removeInteraction(self); m_quit_game() end)) end; keyType = function(self, key) if (GuiMenu.keyType(self, key)) then return true end if (key == "esc") then -- Capture escape to prevent closing the menu return true end end; defaultproperties = { }; } --- NEW FILE: Map.lua --- -- -- The Map class -- By Bjorn Lindeijer import("Object.lua") Map = Object:subclass { name = "Map"; init = function(self, mapName) self.map = m_load_map(mapName) if (not self.map or type(self.map) ~= "userdata") then error("Error while loading map \"".. mapName.."\"!") end -- Call beginPlay on all actors in the map local objs = m_get_objects_on_map(self.map) for k,v in pairs(objs) do if (v:instanceOf(Actor)) then v:beginPlay() end end end; spawn = function(self, class, x, y, owner) return Actor:spawn(class, x, y, self, owner) end; defaultproperties = { map = nil, mapNameBitmap = nil, musicFilename = "", } } --- NEW FILE: MusicController.lua --- -- -- This file contains the music controller. It can be told to play and stop -- songs. As a bonus, it can also dynamically change the parameters of a -- channel over a period of time. Using this feature it can fade from one -- music to another. -- -- By Bjorn Lindeijer MC_NORMAL = 0 MC_FADE_IN = 1 MC_FADE_OUT = 2 MC_FADE_BETWEEN = 3 MusicControl = { state = MC_NORMAL, currentChannel = -1, fadeInChannel = 1, fadeProgress = 0, fadeTime = 0, } function MusicControl:init() self.channels = m_get_number_of_channels() or 0 end function MusicControl:update() if (self.state == MC_FADE_BETWEEN or self.state == MC_FADE_IN or self.state == MC_FADE_OUT) then self.fadeProgress = self.fadeProgress + 1 if (self.state == MC_FADE_BETWEEN) then if (self.fadeProgress > self.fadeTime or self.fadeTime <= 0) then self.state = MC_NORMAL self.fadeProgress = self.fadeTime m_stop_music(self.currentChannel) self.currentChannel = self.fadeInChannel m_adjust_channel(self.currentChannel, 255, 128, 1000) else m_adjust_channel(self.fadeInChannel, (self.fadeProgress / self.fadeTime) * 255, 128, 1000) m_adjust_channel(self.currentChannel, ((self.fadeTime - self.fadeProgress) / self.fadeTime) * 255, 128, 1000) end end if (self.state == MC_FADE_IN) then if (self.fadeProgress > self.fadeTime or self.fadeTime <= 0) then self.state = MC_NORMAL self.fadeProgress = self.fadeTime m_adjust_channel(self.currentChannel, 255, 128, 1000) else m_adjust_channel(self.currentChannel, (self.fadeProgress / self.fadeTime) * 255, 128, 1000) end end if (self.state == MC_FADE_OUT) then if (self.fadeProgress > self.fadeTime or self.fadeTime <= 0) then self.state = MC_NORMAL self.fadeProgress = self.fadeTime m_stop_music(self.currentChannel) self.currentChannel = -1 else m_adjust_channel(self.currentChannel, ((self.fadeTime - self.fadeProgress) / self.fadeTime) * 255, 128, 1000) end end end end function MusicControl:play_song(filename) self:stop_all_music() -- play this song on the first channel self.currentChannel = 0 self.currentSong = filename m_play_music(filename, self.currentChannel) m_adjust_channel(self.currentChannel, 255, 128, 1000) end function MusicControl:stop_all_music() -- stop music on all channels for i = 0,self.channels-1 do m_stop_music(i) end end function MusicControl:fade_to_song(filename, time) if (not (time > 0)) then self:play_song(filename) return end if (self.currentChannel == -1) then self:fade_in(filename, time) return end self.state = MC_FADE_BETWEEN self.fadeTime = time self.fadeProgress = 0 self.fadeInChannel = self.currentChannel + 1 if (self.fadeInChannel == self.channels) then self.fadeInChannel = 0 end -- Start playing music and set volume to 0 self.currentSong = filename m_play_music(filename, self.fadeInChannel) self:update() end function MusicControl:fade_out(time) if (self.currentChannel >= 0) then self.state = MC_FADE_OUT self.fadeTime = time self.fadeProgress = 0 else m_message("MusicControl:fade_out - Warning, no music to fade out") end end function MusicControl:fade_in(filename, time) self:play_song(filename) self.state = MC_FADE_IN self.fadeTime = time self.fadeProgress = 0 m_adjust_channel(self.currentChannel, 0, 128, 1000) end --- NEW FILE: PlayerSwitcher.lua --- -- This class makes it possible to switch between multiple player hosts -- and displays their information on the screen. -- -- By Bjorn Lindeijer import("Interaction.lua") PlayerSwitcher = Interaction:subclass { name = "PlayerSwitcher"; init = function(self, playerController, cameraTarget) self.playerHosts = {} self.playerController = playerController self.cameraTarget = cameraTarget -- Health bar images self.hb_empty = m_get_bitmap("healthbar_empty.bmp") self.hb_full = m_get_bitmap("healthbar_full.bmp") self.hb_w, self.hb_h = m_bitmap_size(self.hb_full) -- Experience bar images self.eb_empty = m_get_bitmap("expbar_empty.bmp") self.eb_full = m_get_bitmap("expbar_full.bmp") self.eb_w, self.eb_h = m_bitmap_size(self.eb_full) end; addPlayerHost = function(self, playerHost) if (not playerHost) then error("Attempt to add non-existing player host!") end table.insert(self.playerHosts, playerHost) if (self.currentHost == 0) then self:selectPlayerHost(playerHost) end end; removePlayerHost = function(self, playerHost) local iIndex = self:hostIndex(playerHost) if (iIndex < 0) then m_message("Attempt to remove non-existing player host.") else table.remove(self.playerHosts, iIndex) if (iIndex == self.currentHost) then if (self.currentHost > table.getn(self.playerHosts)) then self.currentHost = 1 end if (table.getn(self.playerHosts) > 0) then self:selectPlayerHost(self.playerHosts[self.currentHost]) else self.playerController:unPossess() self.currentHost = 0 end end end end; selectPlayerHost = function(self, playerHost) local iIndex = self:hostIndex(playerHost) if (iIndex < 0) then m_message("Attempt to select non-existing player host.") else self.playerController:possess(playerHost) self.currentHost = iIndex self.cameraTarget:setTarget(playerHost) end end; getCurrentHost = function(self) if (self.currentHost > 0) then return self.playerHosts[self.currentHost] end; end; hostIndex = function(self, playerHost) local iIndex -- Search for the Interaction iIndex = -1; for i = 1, table.getn(self.playerHosts) do if (self.playerHosts[i] == playerHost) then iIndex = i end end return iIndex end; keyType = function(self, key) if (key == "tab" and table.getn(self.playerHosts) > 0 and m_get_ex_mode() == 0) then local prevHost = self:getCurrentHost() if (m_get_shift()) then self.currentHost = self.currentHost - 1 if (self.currentHost < 1) then self.currentHost = table.getn(self.playerHosts) end else self.currentHost = self.currentHost + 1 if (self.currentHost > table.getn(self.playerHosts)) then self.currentHost = 1 end end local nextHost = self:getCurrentHost() if (prevHost.map == nextHost.map) then -- Switch player instantly self:selectPlayerHost(self.playerHosts[self.currentHost]) else local musicOut = ActionFadeOutMusic(50) local musicIn = ActionPlaySong(nextHost.myMap.musicFilename, 50) if (nextHost.myMap.musicFilename == MusicControl.currentSong) then musicOut = ActionWait(0) musicIn = ActionWait(0) end -- Switch player after fading out and fade in afterwards ActionController:addSequence{ ActionSetVariable(self, "bActive", false), musicOut, ActionFadeOutMap(50), ActionCallFunction(self.selectPlayerHost, self, self.playerHosts[self.currentHost]), musicIn, ActionFadeInMap(50), ActionSetVariable(self, "bActive", true), } end return true; end; end; tick = function(self) self.cameraTarget:tick() end; preRender = function(self) self.cameraTarget:preRender() end; postRender = function(self, canvas) -- Draw information about each player -- and highlight the selected player for i = 1, table.getn(self.playerHosts) do local player = self.playerHosts[i] local health_perc = player.health / player.maxHealth local experience_perc = player.experience / player.nextLevelExperience local x = 16 + (self.hb_w + 16) * (i - 1) local y = 16 local invHeight = 19 if (table.getn(player.inventory) == 0) then invHeight = 0 end canvas:setDrawMode(DM_TRANS) -- Draw selection block if (i == self.currentHost) then guiTheme:drawBox(x - 3, y - 2, self.hb_w + 6, 14 + 5 + invHeight) end -- Draw the health bar canvas:setAlpha(128) canvas:setCursor(x, y) canvas:drawIcon(self.hb_empty) canvas:setCursor(x, y) canvas:drawPattern(self.hb_full, self.hb_w * health_perc, self.hb_h) -- Draw the experience bar ... [truncated message content] |