[Moeng-cvs] CaveAdventure/data/scripts AI.lua,NONE,1.1 AIBoss.lua,NONE,1.1 AIRandom.lua,NONE,1.1 Gui
Status: Alpha
Brought to you by:
b_lindeijer
Update of /cvsroot/moeng/CaveAdventure/data/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv23103 Added Files: AI.lua AIBoss.lua AIRandom.lua GuiBox.lua GuiMenu.lua GuiMenuItem.lua GuiTheme.lua GuiWidget.lua Log Message: Added some more scripts. --- NEW FILE: AI.lua --- -- -- Our 'magnificent' AI implementation -- -- Common states for an AI monster AI_WAITING = 1 AI_WALKING = 2 AI_ATTACK = 3 AI_DEAD = 4 AI_HIT = 5 AI_READY = 6 CommonAI = {} function CommonAI:event_init() self.state = AI_READY self.tick_time = 1 self.charge = 0 self.charge_time = 200 self.attack_time = 50 self.attack_range = 3 self.attack_min_dam = 1 self.attack_max_dam = 3 self.health = 25 self.maxHealth = self.health end function CommonAI:tick() if (self.charge > 0) then self.charge = self.charge - 1 end -- Switch to ready from walking if (self.state == AI_WALKING and self.walking == 0) then self:setState(AI_READY) end -- When an AI is ready, it's waiting for something to happen to take action if (self.state == AI_READY) then -- Check if player is drawing near playerDist = playerDistance(self) local player = m_get_player() if (playerDist < 5 and player.state ~= CHR_DEAD) then -- Chase or attack? if (playerDist <= self.attack_range) then -- Attack on charged if (self.charge == 0 and self.walking == 0) then self:attack(playerDirection(self)) end else self:walk(playerDirection(self)) end end end end function CommonAI:attack(dir) --m_message("AI attacking!"); self.dir = dir self:setState(AI_ATTACK) local player = m_get_player() -- Handle attack (deal damage to player) player:takeDamage(self.attack_min_dam + math.random(self.attack_max_dam - self.attack_min_dam)) -- Spawn the hitting effect (ie. sparks) if (self.attack_object) then self:attack_object(player) end ActionController:addSequence{ ActionWait(self.attack_time), ActionSetState(self, AI_READY), ActionSetVariable(self, "charge", self.charge_time), } end function CommonAI:walk(dir) m_walk_obj(self, dir) self:setState(AI_WALKING) end function CommonAI:setState(state) self.state = state if (self.state == AI_ATTACK) then self.attacking = 1 else self.attacking = 0 end self:update_bitmap() if (self.state == AI_DEAD) then if (self.do_death) then self:do_death() else self.animation = nil ActionController:addSequence({ ActionWait(100), ActionSetVariable(self, "draw_mode", DM_TRANS), ActionTweenVariable(self, "alpha", 200, 0), ActionDestroyObject(self), }) end self.tick_time = 0 end end function CommonAI:take_damage(amount) if (self.state ~= AI_DEAD) then -- Should probably suspend a little when being hit --self:setState(AI_HIT) self.health = self.health - amount -- Spawn the getting hit effect (ie. blood) if (self.do_hit) then self:do_hit() else local obj = m_add_object(self.x, self.y, "BloodSplat") obj.offset_z = obj.offset_z + 12 end if (self.health <= 0) then self:setState(AI_DEAD) local player = m_get_player() player.experience = player.experience + self.experience if (player.experience >= player.nextLevelExperience) then player.endurance = player.endurance + 5 player.nextLevelExperience = 2.5 * player.nextLevelExperience player:derive_attributes() end end end end --- NEW FILE: AIBoss.lua --- -- -- A modified AI implementation for the boss -- -- Common states for the boss BAI_WAITING = 1 -- Initial state, doing nothing (a sequence will have to set it to BAI_READY BAI_WALKING = 2 -- Though the boss just floats BAI_ATTACK = 3 -- Attacking the player BAI_DEAD = 4 -- Dead BAI_HIT = 5 -- Getting hit by the player BAI_READY = 6 -- Basically in a loop to figure out what to do BAI_PHASE = 7 -- Teleporting to a better spot BossAI = {} function BossAI:event_init() --inherit(self, LinearAni) self.state = AI_READY self.tick_time = 1 self.charge = 0 self.charge_time = 200 self.attack_time = 50 self.attack_range = 3 self.attack_min_dam = 1 self.attack_max_dam = 3 self.health = 100 self.maxHealth = self.health self.teleport_interval = 200 self.teleport_countdown = 0 self.teleport_sequence = nil -- The area in which the boss can teleport self.area_min_x = 31 self.area_min_y = 9 self.area_max_x = 39 self.area_max_y = 16 -- Hovering self.offset_z = 0 self.goal_z = 12 self.count_z = 0 -- Shadow self.shadow = m_add_object(self.x, self.y, "BossShadow") self.shadow.offset_z = -24 end function BossAI:event_destroyed() -- Remove shadow m_destroy_object(self.shadow) end function BossAI:tick() -- Hover up if (self.count_z < self.goal_z) then self.count_z = self.count_z + 0.1 end if (self.count_z > self.goal_z) then self.count_z = self.count_z - 0.1 end self.offset_z = self.count_z -- Keep schadow along with me self.shadow.x = self.x self.shadow.y = self.y - 1 self.shadow.offset_x = self.offset_x self.shadow.offset_y = self.offset_y self.shadow.alpha = self.alpha -- Countdown charge if (self.charge > 0) then self.charge = self.charge - 1 end if (self.teleport_countdown > 0) then self.teleport_countdown = self.teleport_countdown - 1 end -- Switch to ready from walking (or, hovering) if (self.state == AI_WALKING and self.walking == 0) then self:setState(AI_READY) end -- When an boss is ready and player not dead, he's not waiting for something to happen to take action if (self.state == AI_READY and player.state ~= CHR_DEAD) then -- Check if player is drawing near (then, when teleport possible: teleport randomly away to a place not player occupied) playerDist = playerDistance(self) local player = m_get_player() if (playerDist < 2) then if (self.teleport_countdown == 0) then -- We can teleport away, so let's do it! -- Pick a math.random spot (TODO: make sure not close or on top of player) local tx = self.area_min_x + math.random(self.area_max_x - self.area_min_x) local ty = self.area_min_y + math.random(self.area_max_y - self.area_min_y) -- Do the teleport sequence self:setState(BAI_PHASE) self.teleport_sequence = ActionController:addSequence({ ActionSetVariable(self, "draw_mode", DM_TRANS), ActionTweenVariable(self, "alpha", 50, 0), ActionSetPosition(self, tx, ty), ActionTweenVariable(self, "alpha", 50, 255), ActionSetVariable(self, "draw_mode", DM_MASKED), ActionSetVariable(self, "teleport_countdown", math.random(self.teleport_interval) + 100), ActionSetState(self, BAI_READY), }) else -- We could try to hover away self:walk(reverseDirection(playerDirection(self))) end else -- Player not close, so let's cast a spell on him! (muhahaha) if (self.charge == 0) then self:attack(playerDirection(self)) else -- Not charged up, hover randomly! self:walk(math.random(4)) end end end end function BossAI:attack(dir) m_message("AI attacking!"); self.dir = dir self:setState(AI_ATTACK) local player = m_get_player() -- Handle attack (deal damage to player) player:takeDamage(self.attack_min_dam + math.random(self.attack_max_dam - self.attack_min_dam)) -- Spawn the hitting effect (ie. sparks) if (self.attack_object) then self:attack_object(player) end ActionController:addSequence{ ActionWait(self.attack_time), ActionSetState(self, AI_READY), ActionSetVariable(self, "charge", self.charge_time), } end function BossAI:walk(dir) m_walk_obj(self, dir) self:setState(AI_WALKING) end function BossAI:update_bitmap() end function BossAI:setState(state) self.state = state if (self.state == AI_ATTACK) then self.attacking = 1 else self.attacking = 0 end self:update_bitmap() if (self.state == AI_DEAD) then -- Get rid of any teleport sequences ActionController:removeSequence(self.teleport_sequence) --self.tick_time = 0 self:do_death() end end function BossAI:take_damage(amount) if (self.state ~= AI_DEAD) then -- Should probably suspend a little when being hit --self:setState(AI_HIT) self.health = self.health - amount -- Spawn the getting hit effect (ie. blood) if (self.do_hit) then self:do_hit() else local obj = m_add_object(self.x, self.y, "BloodSplat") obj.offset_z = obj.offset_z + 12 end if (self.health <= 0) then self:setState(AI_DEAD) end end end --- NEW FILE: AIRandom.lua --- -- An controller which makes its Pawn walk around randomly -- -- By Bjorn Lindeijer import("Controller.lua") AIRandom = Controller:subclass { name = "AIRandom"; init = function(self) self.waitTime = math.random(100) + 10 end; notifyBumpInto = function(self, obj) -- Pause for some time and choose another direction self.waitTime = math.random(100) + 10 end; notifyWalkFinished = function(self) if (self.distanceToWalk <= 0) then -- Reached his goal, pause and choose new goal. self.waitTime = math.random(100) + 10 else -- Walking to goal, keep walking. self.distanceToWalk = self.distanceToWalk - 1 self.pawn:walk(self.pawn.dir) end end; tick = function(self) if (self.waitTime > 0) then self.waitTime = self.waitTime - 1 if (self.waitTime <= 0) then -- Choose new goal and start walking towards it self.distanceToWalk = math.random(4) self.pawn.dir = math.random(4) - 1 self.pawn:walk(self.pawn.dir) end end end; defaultproperties = { distanceToWalk = 0, }; } --- NEW FILE: GuiBox.lua --- -- -- A box used for use around message windows and menus -- By Bjorn Lindeijer import("Object.lua") GuiBox = Object:subclass { name = "GuiBox"; init = function(x, y, w, h) self.x = x or 0 self.y = y or 0 self.w = w or 0 self.h = h or 0 end; draw = function() end; } --- NEW FILE: GuiMenu.lua --- -- By Bjorn Lindeijer import("Interaction.lua") GuiMenu = Interaction:subclass { name = "GuiMenu"; init = function(self) self.menuItems = {} end; addMenuItem = function(self, menuItem) if (not menuItem) then m_message("Warning: addMenuItem called without a menu item!") return end table.insert(self.menuItems, menuItem) menuItem.menu = self -- Adjust menu size self.h = self.h + menuItem:getHeight() + 1 self.w = math.max(self.w, menuItem:getWidth()) end; postRender = function(self, canvas) if (self.bCenter) then local sw, sh = m_screen_size() self.x = (sw - self.w) / 2 self.y = (sh - self.h) / 2 end m_set_font(guiTheme.font) canvas:setDrawMode(DM_TRANS) local curr = 0 for i = 1, table.getn(self.menuItems) do local tw, th = m_text_size(self.menuItems[i].text) m_set_color(0, 0, 0) m_set_cursor(self.x + (self.w - tw) / 2 + 1, self.y + curr + 1 + 1) m_draw_text(self.menuItems[i].text) curr = curr + self.menuItems[i]:getHeight() + 1 end guiTheme:drawBox(self.x - 2, self.y - 2, self.w + 4, self.h + 3) local curr = 0 for i = 1, table.getn(self.menuItems) do if (i == self.selected) then guiTheme:drawBox(self.x, self.y + curr, self.w, self.menuItems[i]:getHeight()) m_set_color(200, 200, 200) else m_set_color(175, 175, 175) end local tw, th = m_text_size(self.menuItems[i].text) m_set_cursor(self.x + (self.w - tw) / 2, self.y + curr + 1) m_draw_text(self.menuItems[i].text) curr = curr + self.menuItems[i]:getHeight() + 1 end end; keyType = function(self, key) if (key == "up") then self.selected = self.selected - 1 if (self.selected == 0) then self.selected = table.getn(self.menuItems) end m_play_sample("bbsfx_hit1.wav") return true elseif (key == "down") then self.selected = self.selected + 1 if (self.selected > table.getn(self.menuItems)) then self.selected = 1 end m_play_sample("bbsfx_hit1.wav") return true elseif (key == "action") then self.menuItems[self.selected].func() m_play_sample("bbsfx_hit1.wav") return true end end; setInteractionMaster = function(self, master) if (self.master and not master) then ActionController:addAction(ActionExModeOff()) elseif (not self.master and master) then ActionController:addAction(ActionExModeOn()) end Interaction.setInteractionMaster(self, master) end; defaultproperties = { menuItems = nil, h = 0, w = 0, x = 0, y = 0, bCenter = true, selected = 1, } } --- NEW FILE: GuiMenuItem.lua --- import("GuiWidget.lua") GuiMenuItem = GuiWidget:subclass { name = "GuiMenuItem"; init = function(self, text, func) self.text = text self.func = func m_set_font(guiTheme.font) self.w, self.h = m_text_size(self.text) self.w = self.w + 4 self.h = self.h + 2 end; setText = function(self, text) self.text = text m_set_font(guiTheme.font) self.w, self.h = m_text_size(self.text) self.w = self.w + 4 self.h = self.h + 2 if (self.menu) then -- Somehow adapt the size of the menu end end; getWidth = function(self) return self.w end; getHeight = function(self) return self.h end; defaultproperties = { text = "", func = nil, w = 0, h = 0, menu = nil, } } --- NEW FILE: GuiTheme.lua --- -- -- The GUI theme -- By Bjorn Lindeijer import("Object.lua") GuiTheme = Object:subclass { name = "GuiTheme"; init = function(self) self.cornerUL = m_create_sub_bitmap(self.bitmap, 1, 0, 2, 2) self.cornerUR = m_create_sub_bitmap(self.bitmap, 3, 0, 2, 2) self.cornerLL = m_create_sub_bitmap(self.bitmap, 1, 2, 2, 2) self.cornerLR = m_create_sub_bitmap(self.bitmap, 3, 2, 2, 2) self.borderU = m_create_sub_bitmap(self.bitmap, 9, 0, 2, 2) self.borderL = m_create_sub_bitmap(self.bitmap, 11, 0, 2, 2) self.borderR = m_create_sub_bitmap(self.bitmap, 11, 2, 2, 2) self.borderD = m_create_sub_bitmap(self.bitmap, 9, 2, 2, 2) self.bg = m_create_sub_bitmap(self.bitmap, 0, 0, 1, 1) self.shadow = m_create_sub_bitmap(self.bitmap, 0, 1, 1, 1) self.shadowUL = m_create_sub_bitmap(self.bitmap, 5, 0, 2, 2) self.shadowUR = m_create_sub_bitmap(self.bitmap, 7, 0, 2, 2) self.shadowLL = m_create_sub_bitmap(self.bitmap, 5, 2, 2, 2) self.shadowLR = m_create_sub_bitmap(self.bitmap, 7, 2, 2, 2) self.canvas = Canvas() end; drawBox = function(self, x, y, w, h) self.canvas:setDrawMode(DM_TRANS) -- Shadow local alpha = m_set_alpha(64) self:drawBoxEx( self.shadow, self.shadowUL, self.shadowUR, self.shadowLL, self.shadowLR, self.shadow, self.shadow, self.shadow, self.shadow, x+2, y+2, w, h ) -- The actual box m_set_alpha(128) self:drawBoxEx( self.bg, self.cornerUL, self.cornerUR, self.cornerLL, self.cornerLR, self.borderU, self.borderL, self.borderR, self.borderD, x, y, w, h ) m_set_alpha(alpha) end; drawLightBox = function(self, x, y, w, h) self.canvas:setDrawMode(DM_TRANS) local alpha = m_set_alpha(128) self:drawBoxEx( self.bg, self.cornerUL, self.cornerUR, self.cornerLL, self.cornerLR, self.borderU, self.borderL, self.borderR, self.borderD, x, y, w, h ) m_set_alpha(alpha) end; getTextColor = function(self) return 170, 170, 170 end; drawBoxEx = function(self, bg, ul, ur, ll, lr, bu, bl, br, bd, x, y, w, h) m_set_cursor(x, y) self.canvas:drawIcon(ul) self.canvas:drawRect(bu, w - (self.borderWidth * 2), self.borderWidth) self.canvas:drawIcon(ur) m_set_cursor(x, y + self.borderWidth) self.canvas:drawRect(bl, self.borderWidth, h - (self.borderWidth * 2)) self.canvas:drawRect(bg, w - (self.borderWidth * 2), h - (self.borderWidth * 2)) self.canvas:drawRect(br, self.borderWidth, h - (self.borderWidth * 2)) m_set_cursor(x, y + h - self.borderWidth) self.canvas:drawIcon(ll) self.canvas:drawRect(bd, w - (self.borderWidth * 2), self.borderWidth) self.canvas:drawIcon(lr) end; defaultproperties = { bitmap = m_get_bitmap("gui_green.bmp"), font = "font_sansserif8.pcx", borderWidth = 2, canvas = nil, }; } --- NEW FILE: GuiWidget.lua --- -- -- The base class for any GUI widget. -- By Bjorn Lindeijer import("Object.lua") GuiWidget = Object:subclass { name = "GuiWidget"; -- Querying the size of the widget getHeight = function(self) return 0 end; getWidth = function(self) return 0 end; getSize = function(self) return self:getWidth(), self:getHeight() end; } |