diff --git a/game/engine/Dialog.lua b/game/engine/Dialog.lua index 9d11b14ec7db54cbbc005e1ab068bf691ad1cbe4..b0d69b75159e0f9e276e160246e3fe30d4693e6b 100644 --- a/game/engine/Dialog.lua +++ b/game/engine/Dialog.lua @@ -96,7 +96,7 @@ function _M:init(title, w, h, x, y, alpha, font) self.iw, self.ih = w - 2 * 5, h - 8 - 16 - 3 self.internal_surface = core.display.newSurface(self.iw, self.ih) self.surface:alpha(alpha or 220) - self.texture = self.surface:glTexture() + self.texture, self.texture_w, self.texture_h = self.surface:glTexture() self.changed = true end @@ -123,35 +123,42 @@ function _M:display() local tw, th = self.font:size(self.title) s:drawColorStringBlended(self.font, self.title, (self.w - tw) / 2, 4, 255,255,255) - + self.internal_surface:erase() self:drawDialog(self.internal_surface) s:merge(self.internal_surface, 5, 20 + 3) + -- Update texture + self.surface:updateTexture(self.texture) + return self.surface end -function _M:addControl(control) +function _M:toScreen(x, y) + self.texture:toScreenFull(x, y, self.w, self.h, self.texture_w, self.texture_h) +end + +function _M:addControl(control) control.tabindex = self.tabindex - self.tabindex = self.tabindex + 1 - self.controls[control.name] = control + self.tabindex = self.tabindex + 1 + self.controls[control.name] = control table.sort(self.controls, function(a,b) return a.tabindex<b.tabindex end) end function _M:changeFocus(up) - local add = 1 + local add = 1 if not up then add = -1 end self.currenttabindex = self.currenttabindex + add - if (self.currenttabindex==self.tabindex) then self.currenttabindex = 0 end + if (self.currenttabindex==self.tabindex) then self.currenttabindex = 0 end if self.currenttabindex==-1 then self.currenttabindex=self.tabindex-1 end local name = "" for i, cntrl in pairs(self.controls) do - if cntrl.tabindex==self.currenttabindex then + if cntrl.tabindex==self.currenttabindex then if self.controls[self.state] and self.controls[self.state].unFocus then self.controls[self.state]:unFocus() end - cntrl.focused=true - name=i + cntrl.focused=true + name=i end - end + end return name end @@ -161,29 +168,25 @@ function _M:focusControl(focusOn) for i, cntrl in pairs(self.controls) do if i==focusOn then cntrl.focused=true self.state=i self.currenttabindex=cntrl.tabindex end if i==oldstate and cntrl.unFocus then cntrl:unFocus() end - end + end end function _M:databind() - local result = { } - for i, cntrl in pairs(self.controls or { }) do + local result = { } + for i, cntrl in pairs(self.controls or { }) do if cntrl.type and cntrl.type=="TextBox" then result[cntrl.name] = cntrl.text end - end + end return result end -function _M:drawControls(s) - for i, cntrl in pairs(self.controls or { }) do +function _M:drawControls(s) + for i, cntrl in pairs(self.controls or { }) do cntrl:drawControl(s) - end -end - -function _M:toScreen(x,y) - self.surface:toScreenWithTexture(self.texture,x,y) + end end function _M:drawDialog(s) diff --git a/game/modules/tome/class/interface/Archery.lua b/game/modules/tome/class/interface/Archery.lua new file mode 100644 index 0000000000000000000000000000000000000000..61a93581aba350f33b4ab3e6cc04afba35b011b7 --- /dev/null +++ b/game/modules/tome/class/interface/Archery.lua @@ -0,0 +1,201 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +local DamageType = require "engine.DamageType" +local Map = require "engine.Map" +local Chat = require "engine.Chat" +local Target = require "engine.Target" +local Talents = require "engine.interface.ActorTalents" + +--- Interface to add ToME archery combat system +module(..., package.seeall, class.make) + +--- Look for possible archery targets +-- Take care of removing enough ammo +function _M:archeryAcquireTargets(tg, params) + local weapon, ammo = self:hasArcheryWeapon() + if not weapon then + game.logPlayer(self, "You must wield a bow or a sling (%s)!", ammo) + return nil + end + params = params or {} + + print("[ARCHERY AQUIRE TARGETS WITH]", weapon.name, ammo.name) + local realweapon = weapon + weapon = weapon.combat + + local tg = tg or {type="bolt"} + + if not tg.range then tg.range=weapon.range or 10 end + tg.display = tg.display or {display='/'} + tg.speed = tg.speed or 20 + local x, y = self:getTarget(tg) + if not x or not y then return nil end + + -- Find targets to know how many ammo we use + local targets = {} + if params.one_shot then + local ammo = self:removeObject(self:getInven("QUIVER"), 1) + if ammo then + targets = {{x=x, y=y, ammo=ammo.combat}} + end + else + local limit_shots = params.limit_shots + + self:project(tg, x, y, function(tx, ty) + local target = game.level.map(tx, ty, game.level.map.ACTOR) + if not target then return end + if tx == self.x and ty == self.y then return end + + if limit_shots then + if limit_shots <= 0 then return end + limit_shots = limit_shots - 1 + end + + for i = 1, params.multishots or 1 do + local ammo = self:removeObject(self:getInven("QUIVER"), 1) + if ammo then targets[#targets+1] = {x=tx, y=ty, ammo=ammo.combat} + else break end + end + end) + end + + if #targets > 0 then + local sound = weapon.sound + + local speed = self:combatSpeed(weapon) + print("[SHOOT] speed", speed or 1, "=>", game.energy_to_act * (speed or 1)) + self:useEnergy(game.energy_to_act * (speed or 1)) + + if sound then game:playSoundNear(targets[1], sound) end + + if ammo:getNumber() < 10 or ammo:getNumber() == 50 or ammo:getNumber() == 40 or ammo:getNumber() == 25 then + game.logPlayer(self, "You only have %d %s left!", ammo:getNumber(), ammo.name) + end + + return targets + else + return nil + end +end + +--- Archery projectile code +local function archery_projectile(tx, ty, tg, self) + local weapon, ammo = tg.archery.weapon, tg.archery.ammo + + local target = game.level.map(tx, ty, game.level.map.ACTOR) + if not target then return end + + local talent = self:getTalentFromId(tg.talent_id) + + local damtype = tg.archery.damtype or ammo.damtype or DamageType.PHYSICAL + local mult = tg.archery.mult or 1 + + -- Does the blow connect? yes .. complex :/ + local atk, def = self:combatAttack(weapon), target:combatDefenseRanged() + local dam, apr, armor = self:combatDamage(ammo), self:combatAPR(ammo), target:combatArmor() + print("[ATTACK ARCHERY] to ", target.name, " :: ", dam, apr, armor, "::", mult) + if not self:canSee(target) then atk = atk / 3 end + + -- If hit is over 0 it connects, if it is 0 we still have 50% chance + local hitted = false + if self:checkHit(atk, def) then + apr = apr + (tg.archery.apr or 0) + print("[ATTACK ARCHERY] raw dam", dam, "versus", armor, "with APR", apr) + + local dam = math.max(0, dam - math.max(0, armor - apr)) + local damrange = self:combatDamageRange(ammo) + dam = rng.range(dam, dam * damrange) + print("[ATTACK ARCHERY] after range", dam) + + local crit + if tg.archery.crit_chance then self.combat_physcrit = self.combat_physcrit + tg.archery.crit_chance end + dam, crit = self:physicalCrit(dam, ammo, target) + if tg.archery.crit_chance then self.combat_physcrit = self.combat_physcrit - tg.archery.crit_chance end + print("[ATTACK ARCHERY] after crit", dam) + + dam = dam * mult + print("[ATTACK ARCHERY] after mult", dam) + + if crit then game.logSeen(self, "%s performs a critical strike!", self.name:capitalize()) end + DamageType:get(damtype).projector(self, target.x, target.y, damtype, math.max(0, dam)) + game.level.map:particleEmitter(target.x, target.y, 1, "archery") + hitted = true + + if talent.archery_onhit then talent.archery_onhit(self, talent, target, target.x, target.y) end + else + local srcname = game.level.map.seens(self.x, self.y) and self.name:capitalize() or "Something" + game.logSeen(target, "%s misses %s.", srcname, target.name) + end + + -- Ranged project + if hitted and not target.dead then for typ, dam in pairs(self.ranged_project) do + if dam > 0 then + DamageType:get(typ).projector(self, target.x, target.y, typ, dam) + end + end end + + -- Regen on being hit + if hitted and not target.dead and target:attr("stamina_regen_on_hit") then target:incStamina(target.stamina_regen_on_hit) end + if hitted and not target.dead and target:attr("mana_regen_on_hit") then target:incMana(target.mana_regen_on_hit) end +end + +--- Shoot at one target +function _M:archeryShoot(targets, talent, tg, params) + local weapon, ammo = self:hasArcheryWeapon() + if not weapon then + game.logPlayer(self, "You must wield a bow or a sling (%s)!", ammo) + return nil + end + + print("[SHOOT WITH]", weapon.name, ammo.name) + local realweapon = weapon + weapon = weapon.combat + + local tg = tg or {type="bolt"} + tg.talent = tg.talent or talent + + if not tg.range then tg.range=weapon.range or 10 end + tg.display = tg.display or {display='/'} + tg.speed = tg.speed or 20 + tg.archery = params or {} + tg.archery.weapon = weapon + for i = 1, #targets do + local tg = table.clone(tg) + tg.archery.ammo = targets[i].ammo + print("******........ firing target", targets[i].x, targets[i].y, "from", self.x, self.y) + self:projectile(tg, targets[i].x, targets[i].y, archery_projectile) + end +end + +--- Check if the actor has a bow or sling and corresponding ammo +function _M:hasArcheryWeapon() + if not self:getInven("MAINHAND") then return nil, "no shooter" end + if not self:getInven("QUIVER") then return nil, "no ammo" end + local weapon = self:getInven("MAINHAND")[1] + local ammo = self:getInven("QUIVER")[1] + if not weapon or not weapon.archery then + return nil, "no shooter" + end + if not ammo or not ammo.archery_ammo or weapon.archery ~= ammo.archery_ammo then + return nil, "bad or no ammo" + end + return weapon, ammo +end diff --git a/src/core_lua.c b/src/core_lua.c index 89e81388a4b9071a2019276f62fcc9c38fc99716..84436712ca0174ecb2590b723aa0a8ed65eb3472 100644 --- a/src/core_lua.c +++ b/src/core_lua.c @@ -820,7 +820,7 @@ static GLenum sdl_gl_texture_format(SDL_Surface *s) { // allocate memory for a texture without copying pixels in // caller binds texture -static void make_texture_for_surface(SDL_Surface *s) { +static void make_texture_for_surface(SDL_Surface *s, int *fw, int *fh) { // Paramétrage de la texture. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); @@ -837,6 +837,8 @@ static void make_texture_for_surface(SDL_Surface *s) { while (realw < s->w) realw *= 2; while (realh < s->h) realh *= 2; + if (fw) *fw = realw; + if (fh) *fh = realh; //printf("request size (%d,%d), producing size (%d,%d)\n",s->w,s->h,realw,realh); glTexImage2D(GL_TEXTURE_2D, 0, nOfColors, realw, realh, 0, texture_format, GL_UNSIGNED_BYTE, NULL); @@ -877,7 +879,7 @@ static int sdl_surface_toscreen(lua_State *L) glGenTextures(1, &t); glBindTexture(GL_TEXTURE_2D, t); - make_texture_for_surface(*s); + make_texture_for_surface(*s, NULL, NULL); copy_surface_to_texture(*s); draw_textured_quad(x,y,(*s)->w,(*s)->h); @@ -913,6 +915,17 @@ static int sdl_surface_toscreen_with_texture(lua_State *L) return 0; } +static int sdl_surface_update_texture(lua_State *L) +{ + SDL_Surface **s = (SDL_Surface**)auxiliar_checkclass(L, "sdl{surface}", 1); + GLuint *t = (GLuint*)auxiliar_checkclass(L, "gl{texture}", 2); + + glBindTexture(GL_TEXTURE_2D, *t); + copy_surface_to_texture(*s); + + return 0; +} + static int sdl_surface_to_texture(lua_State *L) { SDL_Surface **s = (SDL_Surface**)auxiliar_checkclass(L, "sdl{surface}", 1); @@ -923,10 +936,14 @@ static int sdl_surface_to_texture(lua_State *L) glGenTextures(1, t); glBindTexture(GL_TEXTURE_2D, *t); - make_texture_for_surface(*s); + int fw, fh; + make_texture_for_surface(*s, &fw, &fh); copy_surface_to_texture(*s); - return 1; + lua_pushnumber(L, fw); + lua_pushnumber(L, fh); + + return 3; } static int sdl_surface_merge(lua_State *L) @@ -986,6 +1003,39 @@ static int sdl_texture_toscreen(lua_State *L) return 0; } +static int sdl_texture_toscreen_full(lua_State *L) +{ + GLuint *t = (GLuint*)auxiliar_checkclass(L, "gl{texture}", 1); + int x = luaL_checknumber(L, 2); + int y = luaL_checknumber(L, 3); + int w = luaL_checknumber(L, 4); + int h = luaL_checknumber(L, 5); + int rw = luaL_checknumber(L, 6); + int rh = luaL_checknumber(L, 7); + if (lua_isnumber(L, 8)) + { + float r = luaL_checknumber(L, 9); + float g = luaL_checknumber(L, 10); + float b = luaL_checknumber(L, 11); + float a = luaL_checknumber(L, 12); + glColor4f(r, g, b, a); + } + + glBindTexture(GL_TEXTURE_2D, *t); + GLfloat texw = (GLfloat)w/rw; + GLfloat texh = (GLfloat)h/rh; + + glBegin( GL_QUADS ); + glTexCoord2f(0,0); glVertex2f(0 + x, 0 + y); + glTexCoord2f(0,texh); glVertex2f(0 + x, h + y); + glTexCoord2f(texw,texh); glVertex2f(w + x, h + y); + glTexCoord2f(texw,0); glVertex2f(w + x, 0 + y); + glEnd( ); + + if (lua_isnumber(L, 8)) glColor4f(1, 1, 1, 1); + return 0; +} + static bool _CheckGL_Error(const char* GLcall, const char* file, const int line) { GLenum errCode; @@ -1294,6 +1344,7 @@ static const struct luaL_reg sdl_surface_reg[] = {"merge", sdl_surface_merge}, {"toScreen", sdl_surface_toscreen}, {"toScreenWithTexture", sdl_surface_toscreen_with_texture}, + {"updateTexture", sdl_surface_update_texture}, {"putChar", lua_display_char}, {"drawString", sdl_surface_drawstring}, {"drawStringBlended", sdl_surface_drawstring_aa}, @@ -1307,6 +1358,7 @@ static const struct luaL_reg sdl_texture_reg[] = {"__gc", sdl_free_texture}, {"close", sdl_free_texture}, {"toScreen", sdl_texture_toscreen}, + {"toScreenFull", sdl_texture_toscreen_full}, {"makeOutline", sdl_texture_outline}, {"toSurface", gl_texture_to_sdl}, {NULL, NULL},