From 9eebbb12858f83e7581717b6da1ef1de26225b43 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Wed, 23 Jun 2010 14:27:52 +0000 Subject: [PATCH] added unique shaders Refitted all damage formulas to use more explicit and generic (and bound) functions git-svn-id: http://svn.net-core.org/repos/t-engine4@831 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/modules/tome/class/Game.lua | 2 +- game/modules/tome/class/interface/Combat.lua | 7 + .../tome/data/gfx/shaders/unique_glow.frag | 46 ++ .../tome/data/gfx/shaders/unique_glow.lua | 28 + .../tome/data/talents/cunning/dirty.lua | 8 +- .../tome/data/talents/cunning/lethality.lua | 4 +- .../tome/data/talents/divine/chants.lua | 24 +- .../tome/data/talents/divine/combat.lua | 8 +- .../tome/data/talents/divine/glyphs.lua | 4 +- .../tome/data/talents/divine/hymns.lua | 22 +- .../tome/data/talents/divine/light.lua | 12 +- .../tome/data/talents/divine/star-fury.lua | 20 +- game/modules/tome/data/talents/divine/sun.lua | 18 +- game/modules/tome/data/talents/misc/npcs.lua | 32 +- .../tome/data/talents/spells/arcane.lua | 13 +- .../tome/data/talents/spells/conveyance.lua | 4 +- .../tome/data/talents/spells/earth.lua | 6 +- .../tome/data/talents/spells/enhancement.lua | 20 +- .../tome/data/talents/spells/nature.lua | 8 +- .../tome/data/talents/spells/phantasm.lua | 12 +- .../tome/data/talents/spells/temporal.lua | 4 +- .../tome/data/talents/spells/water.lua | 16 +- .../tome/data/talents/techniques/2hweapon.lua | 24 +- .../tome/data/talents/techniques/archery.lua | 20 +- .../tome/data/talents/techniques/bow.lua | 12 +- .../data/talents/techniques/dualweapon.lua | 26 +- .../tome/data/talents/techniques/sling.lua | 12 +- .../data/talents/techniques/weaponshield.lua | 22 +- src/core_lua.c | 3 +- src/sdnoise1234.c | 752 ++++++++++++++++++ src/sdnoise1234.h | 84 ++ 31 files changed, 1097 insertions(+), 176 deletions(-) create mode 100644 game/modules/tome/data/gfx/shaders/unique_glow.frag create mode 100644 game/modules/tome/data/gfx/shaders/unique_glow.lua create mode 100644 src/sdnoise1234.c create mode 100644 src/sdnoise1234.h diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 5fbc605e98..166995dcb4 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -526,7 +526,7 @@ function _M:setupCommands() [{"_d","ctrl"}] = function() if config.settings.tome.cheat then self.player:forceLevelup(50) - self:changeLevel(1, "rak-shor-pride") + self:changeLevel(1, "moria") end end, } diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index cad19d70eb..0dd97c145d 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -493,6 +493,13 @@ function _M:combatTalentSpellDamage(t, base, max) return (base + self:combatSpellpower()) * ((math.sqrt(self:getTalentLevel(t)) - 1) * 0.8 + 1) * mod end +--- Gets weapon damage mult based on talent +function _M:combatTalentWeaponDamage(t, base, max, t2) + if t2 then t2 = t2 / 2 else t2 = 0 end + local diff = max - base + return base + diff * math.sqrt((self:getTalentLevel(t) + t2) / 5) +end + --- Gets spellcrit function _M:combatSpellCrit() return self.combat_spellcrit + (self:getCun() - 10) * 0.3 + (self:getLck() - 50) * 0.30 + 1 diff --git a/game/modules/tome/data/gfx/shaders/unique_glow.frag b/game/modules/tome/data/gfx/shaders/unique_glow.frag new file mode 100644 index 0000000000..cdb516d0ab --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/unique_glow.frag @@ -0,0 +1,46 @@ +uniform vec2 texSize; +uniform sampler2D tex; +uniform sampler3D noiseVol; +uniform vec4 color; +uniform float tick; + +int blursize = 2; + +void main(void) +{ + float fTime0_1 = tick / 1000; + vec2 offset = 1.0/texSize; + + // Center Pixel + vec4 sample = vec4(0.0,0.0,0.0,0.0); + vec4 center = texture2D(tex, vec2(gl_TexCoord[0].st)); + float factor = ((float(blursize)*2.0)+1.0); + factor = factor*factor; + + for(int i = -blursize; i <= blursize; i++) + { + for(int j = -blursize; j <= blursize; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + + // keycolor trick + float a = 1.0-center.a; + // float a = 1.0-center.r; + + float delta = sample.a; + // float delta = max(max(sample.r,sample.g),sample.b)/factor; + float noise = texture3D(noiseVol, vec3(gl_TexCoord[0].xy,fTime0_1)).r; + gl_FragColor = mix(center,delta*color*noise,a); + + /* + float delta = sample.a; + // float delta = max(max(sample.r,sample.g),sample.b)/factor; + gl_FragColor = mix(center,delta*color,a); + */ + + /* + gl_FragColor = mix(center,sample/factor,a); + */ +} diff --git a/game/modules/tome/data/gfx/shaders/unique_glow.lua b/game/modules/tome/data/gfx/shaders/unique_glow.lua new file mode 100644 index 0000000000..e4f6a6889b --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/unique_glow.lua @@ -0,0 +1,28 @@ +-- 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 + +return { + frag = "unique_glow", + vert = nil, + args = { + color = color or {1,1,1,1}, + texSize = size or {32, 32}, + }, + clone = false, +} diff --git a/game/modules/tome/data/talents/cunning/dirty.lua b/game/modules/tome/data/talents/cunning/dirty.lua index 485aa0977e..0e97f3b5b3 100644 --- a/game/modules/tome/data/talents/cunning/dirty.lua +++ b/game/modules/tome/data/talents/cunning/dirty.lua @@ -31,7 +31,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hitted = self:attackTarget(target, nil, 0.2 + self:getTalentLevel(t) / 12, true) + local hitted = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.2, 0.7), true) if hitted then if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then @@ -45,7 +45,7 @@ newTalent{ end, info = function(self, t) return ([[You hit your target doing %d%% damage, trying to stun it instead of damaging it. If your attack hits, the target is stunned for %d turns.]]): - format(100 * (0.2 + self:getTalentLevel(t) / 12), 3 + math.ceil(self:getTalentLevel(t))) + format(100 * self:combatTalentWeaponDamage(t, 0.2, 0.7), 3 + math.ceil(self:getTalentLevel(t))) end, } @@ -107,7 +107,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hitted = self:attackTarget(target, nil, 0.9 + self:getTalentLevel(t) / 9, true) + local hitted = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.9, 1.4), true) if hitted then if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) then @@ -128,6 +128,6 @@ newTalent{ end, info = function(self, t) return ([[You hit your target doing %d%% damage. If your attack hits, the target is crippled for %d turns, losing %d%% attack and %d%% damage.]]): - format(100 * (0.9 + self:getTalentLevel(t) / 9), 3 + math.ceil(self:getTalentLevel(t)), 10 + self:getTalentLevel(t) * 3, 10 + self:getTalentLevel(t) * 4) + format(100 * self:combatTalentWeaponDamage(t, 0.9, 1.4), 3 + math.ceil(self:getTalentLevel(t)), 10 + self:getTalentLevel(t) * 3, 10 + self:getTalentLevel(t) * 4) end, } diff --git a/game/modules/tome/data/talents/cunning/lethality.lua b/game/modules/tome/data/talents/cunning/lethality.lua index 320a42fc13..d40047f0b1 100644 --- a/game/modules/tome/data/talents/cunning/lethality.lua +++ b/game/modules/tome/data/talents/cunning/lethality.lua @@ -41,7 +41,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hitted = self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 10, true) + local hitted = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.8, 1.4), true) if hitted then local dur = 5 + math.ceil(self:getTalentLevel(t)) @@ -54,7 +54,7 @@ newTalent{ info = function(self, t) return ([[You hit your target doing %d%% damage. If your attack hits, you gain %d armour penetration for %d turns. The APR will increase with Cunning.]]): - format(100 * (0.8 + self:getTalentLevel(t) / 10), 4 + (self:getTalentLevel(t) * self:getCun()) / 20, 5 + math.ceil(self:getTalentLevel(t))) + format(100 * self:combatTalentWeaponDamage(t, 0.8, 1.4), 4 + (self:getTalentLevel(t) * self:getCun()) / 20, 5 + math.ceil(self:getTalentLevel(t))) end, } diff --git a/game/modules/tome/data/talents/divine/chants.lua b/game/modules/tome/data/talents/divine/chants.lua index 9f44f65634..ade7f1451a 100644 --- a/game/modules/tome/data/talents/divine/chants.lua +++ b/game/modules/tome/data/talents/divine/chants.lua @@ -43,8 +43,8 @@ newTalent{ range = 20, activate = function(self, t) cancelChants(self) - local power = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.12) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local power = self:combatTalentSpellDamage(t, 5, 70) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=dam}), @@ -65,7 +65,7 @@ newTalent{ return ([[Chant the glory of the sun, granting you %d physical and spell resistance. In addition it surrounds you with a shield of light, damaging anything that attacks you for %0.2f light damage. You may only have one Chant active at once. - The resistance and damage will increase with the Magic stat]]):format(5 + self:getTalentLevel(t) * self:combatSpellpower(0.12), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The resistance and damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 70), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -83,8 +83,8 @@ newTalent{ range = 20, activate = function(self, t) cancelChants(self) - local power = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.08) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local power = self:combatTalentSpellDamage(t, 5, 35) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=dam}), @@ -103,7 +103,7 @@ newTalent{ return ([[Chant the glory of the sun, granting you %d%% physical damage resistance. In addition it surrounds you with a shield of light, damaging anything that attacks you for %0.2f light damage. You may only have one Chant active at once. - The resistance and damage will increase with the Magic stat]]):format(5 + self:getTalentLevel(t) * self:combatSpellpower(0.08), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The resistance and damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 35), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -121,8 +121,8 @@ newTalent{ range = 20, activate = function(self, t) cancelChants(self) - local power = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.08) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local power = self:combatTalentSpellDamage(t, 5, 35) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=dam}), @@ -146,7 +146,7 @@ newTalent{ return ([[Chant the glory of the sun, granting you %d%% elemental resistances. In addition it surrounds you with a shield of light, damaging anything that attacks you for %0.2f light damage. You may only have one Chant active at once. - The resistance and damage will increase with the Magic stat]]):format(5 + self:getTalentLevel(t) * self:combatSpellpower(0.08), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The resistance and damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 35), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -164,8 +164,8 @@ newTalent{ range = 20, activate = function(self, t) cancelChants(self) - local power = 10 + self:getTalentLevel(t) * self:combatSpellpower(0.10) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local power = self:combatTalentSpellDamage(t, 10, 50) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=dam}), @@ -184,6 +184,6 @@ newTalent{ return ([[Chant the glory of the sun, granting you %d%% more light damage. In addition it surrounds you with a shield of light, damaging anything that attacks you for %0.2f light damage. You may only have one Chant active at once. - The damage will increase with the Magic stat]]):format(10 + self:getTalentLevel(t) * self:combatSpellpower(0.10), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 50), self:combatTalentSpellDamage(t, 5, 25)) end, } diff --git a/game/modules/tome/data/talents/divine/combat.lua b/game/modules/tome/data/talents/divine/combat.lua index fb635b3285..d45e497584 100644 --- a/game/modules/tome/data/talents/divine/combat.lua +++ b/game/modules/tome/data/talents/divine/combat.lua @@ -98,7 +98,7 @@ newTalent{ local _ _, x, y = self:canProject(tg, x, y) local target = game.level.map(x, y, Map.ACTOR) if target then - self:attackTarget(target, nil, 1.1 + self:getTalentLevel(t) / 7, true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1.1, 1.9), true) else return end @@ -107,7 +107,7 @@ newTalent{ info = function(self, t) return ([[In a pure display of power you project a melee attack up to a range of %d, doing %d%% damage. The range will increase with the Strength stat]]): - format(self:getTalentRange(t), 100 * (1.1 + self:getTalentLevel(t) / 7)) + format(self:getTalentRange(t), 100 * self:combatTalentWeaponDamage(t, 1.1, 1.9)) end, } @@ -127,11 +127,11 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - self:attackTarget(target, DamageType.LIGHT, 1.1 + self:getTalentLevel(t) / 7, true) + self:attackTarget(target, DamageType.LIGHT, self:combatTalentWeaponDamage(t, 1.1, 1.9), true) return true end, info = function(self, t) return ([[Concentrate the power of the sun in a single blow doing %d%% light damage.]]): - format(100 * (1.1 + self:getTalentLevel(t) / 7)) + format(100 * self:combatTalentWeaponDamage(t, 1.1, 1.9)) end, } diff --git a/game/modules/tome/data/talents/divine/glyphs.lua b/game/modules/tome/data/talents/divine/glyphs.lua index cb978426cf..f59e78f427 100644 --- a/game/modules/tome/data/talents/divine/glyphs.lua +++ b/game/modules/tome/data/talents/divine/glyphs.lua @@ -77,7 +77,7 @@ newTalent{ cooldown = 20, positive = -10, action = function(self, t) - local dam = 15 + self:combatSpellpower(0.37) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 20, 150) local trap = Trap.new{ name = "glyph of explosion", type = "elemental", id_by_type=true, unided_name = "trap", @@ -116,7 +116,7 @@ newTalent{ info = function(self, t) return ([[You bind light in a glyph on the floor, the first target passing by will be hit by a blast of light doing %0.2f damage. The glyph lasts for %d turns. - The damage will increase with the Magic stat]]):format(15 + self:combatSpellpower(0.37) * self:getTalentLevel(t), 2 + self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 20, 150), 2 + self:getTalentLevel(t)) end, } diff --git a/game/modules/tome/data/talents/divine/hymns.lua b/game/modules/tome/data/talents/divine/hymns.lua index 2be5419068..265f09b8b2 100644 --- a/game/modules/tome/data/talents/divine/hymns.lua +++ b/game/modules/tome/data/talents/divine/hymns.lua @@ -43,8 +43,8 @@ newTalent{ range = 20, activate = function(self, t) cancelHymns(self) - local power = 10 + self:getTalentLevel(t) * self:combatSpellpower(0.10) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local power = self:combatTalentSpellDamage(t, 10, 50) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.DARKNESS]=dam}), @@ -63,7 +63,7 @@ newTalent{ return ([[Chant the glory of the moon, granting you %d%% more darkness damage. In addition it surrounds you with a shield of shadows, damaging anything that attacks you for %0.2f darkness damage. You may only have one Hymn active at once. - The damage will increase with the Magic stat]]):format(10 + self:getTalentLevel(t) * self:combatSpellpower(0.10), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 50), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -81,8 +81,10 @@ newTalent{ range = 20, activate = function(self, t) cancelHymns(self) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { + onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.DARKNESS]=dam}), infravision = self:addTemporaryValue("infravision", math.floor(5 + self:getTalentLevel(t))), particle = self:addParticles(Particles.new("darkness_shield", 1)) } @@ -90,14 +92,15 @@ newTalent{ end, deactivate = function(self, t, p) self:removeParticles(p.particle) + self:removeTemporaryValue("on_melee_hit", p.onhit) self:removeTemporaryValue("infravision", p.infravision) return true end, info = function(self, t) return ([[Chant the glory of the moon, granting you infravision up to %d grids. - In addition it surrounds you with a shield of darkness, damaging anything that attacks you for %0.2f light damage. + In addition it surrounds you with a shield of darkness, damaging anything that attacks you for %0.2f darkness damage. You may only have one Hymn active at once. - The resistance and damage will increase with the Magic stat]]):format(math.floor(5 + self:getTalentLevel(t)), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The resistance and damage will increase with the Magic stat]]):format(math.floor(5 + self:getTalentLevel(t)), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -115,8 +118,7 @@ newTalent{ range = 20, activate = function(self, t) cancelHymns(self) - local power = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.08) - local dam = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07) + local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.DARKNESS]=dam}), @@ -139,7 +141,7 @@ newTalent{ return ([[Chant the glory of the moon, granting you %d%% stun, blindness and confusion resistances. In addition it surrounds you with a shield of darkness, damaging anything that attacks you for %0.2f light damage. You may only have one Hymn active at once. - The damage will increase with the Magic stat]]):format(100 * (0.2 + self:getTalentLevel(t) / 10), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.07)) + The damage will increase with the Magic stat]]):format(100 * (0.2 + self:getTalentLevel(t) / 10), self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -180,7 +182,7 @@ newTalent{ local a, id = rng.table(tgts) table.remove(tgts, id) - self:project(tg, a.x, a.y, DamageType.DARKNESS, rng.avg(1, self:spellCrit(20 + self:combatSpellpower(0.2) * self:getTalentLevel(t)), 3)) + self:project(tg, a.x, a.y, DamageType.DARKNESS, rng.avg(1, self:spellCrit(self:combatTalentSpellDamage(t, 7, 80)), 3)) game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "shadow_beam", {tx=a.x-self.x, ty=a.y-self.y}) game:playSoundNear(self, "talents/spell_generic") end @@ -202,6 +204,6 @@ newTalent{ return ([[Conjures a shroud of dancing shadows with a radius of 5 that follows you as long as this spell is active. Each turn a random shadow beam will hit up to %d of your foes for 1 to %0.2f damage. This powerful spell will continuously drain negative energy while active. - The damage will increase with the Magic stat]]):format(self:getTalentLevel(t), 20 + self:combatSpellpower(0.2) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:getTalentLevel(t), self:combatTalentSpellDamage(t, 7, 80)) end, } diff --git a/game/modules/tome/data/talents/divine/light.lua b/game/modules/tome/data/talents/divine/light.lua index 30aaf8a491..b224f803e6 100644 --- a/game/modules/tome/data/talents/divine/light.lua +++ b/game/modules/tome/data/talents/divine/light.lua @@ -28,13 +28,13 @@ newTalent{ HEAL = 10, }, action = function(self, t) - self:heal(self:spellCrit(20 + self:combatSpellpower(0.5) * self:getTalentLevel(t)), self) + self:heal(self:spellCrit(self:combatTalentSpellDamage(t, 20, 240)), self) game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) return ([[An invigorating ray of sun shines on you, healing your body for %d life. - The life healed will increase with the Magic stat]]):format(20 + self:combatSpellpower(0.5) * self:getTalentLevel(t)) + The life healed will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 20, 240)) end, } @@ -51,7 +51,7 @@ newTalent{ action = function(self, t) local duration = self:getTalentLevel(t) + 2 local radius = 3 - local dam = 5 + self:combatSpellpower(0.20) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 4, 20) local tg = {type="ball", range=self:getTalentRange(t), radius=radius} -- Add a lasting map effect game.level.map:addEffect(self, @@ -67,7 +67,7 @@ newTalent{ end, info = function(self, t) return ([[A magical zone of sunlight appears around you, healing all that stand within. - The life healed will increase with the Magic stat]]):format(5 + self:combatSpellpower(0.20) * self:getTalentLevel(t)) + The life healed will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 4, 20)) end, } @@ -79,12 +79,12 @@ newTalent{ positive = -20, cooldown = 60, action = function(self, t) - self:setEffect(self.EFF_DAMAGE_SHIELD, 10, {power=(10 + self:getMag(30)) * self:getTalentLevel(t)}) + self:setEffect(self.EFF_DAMAGE_SHIELD, 10, {power=self:combatTalentSpellDamage(t, 30, 170)}) game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) - return ([[A protective shield forms around you, negating %d damage.]]):format((10 + self:getMag(30)) * self:getTalentLevel(t)) + return ([[A protective shield forms around you, negating %d damage.]]):format(self:combatTalentSpellDamage(t, 30, 170)) end, } diff --git a/game/modules/tome/data/talents/divine/star-fury.lua b/game/modules/tome/data/talents/divine/star-fury.lua index 934e039654..87b9ced3ac 100644 --- a/game/modules/tome/data/talents/divine/star-fury.lua +++ b/game/modules/tome/data/talents/divine/star-fury.lua @@ -33,7 +33,7 @@ newTalent{ local tg = {type="beam", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(14 + self:combatSpellpower(0.5) * self:getTalentLevel(t))) + self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(self:combatTalentSpellDamage(t, 14, 230))) local _ _, x, y = self:canProject(tg, x, y) game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "shadow_beam", {tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/flame") @@ -41,7 +41,7 @@ newTalent{ end, info = function(self, t) return ([[Calls the power of the Moon into a beam of shadows doing %0.2f damage. - The damage will increase with the Magic stat]]):format(14 + self:combatSpellpower(0.5) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 14, 230)) end, } @@ -59,12 +59,12 @@ newTalent{ action = function(self, t) local duration = self:getTalentLevel(t) + 2 local radius = 3 - local dam = 4 + self:combatSpellpower(0.12) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 4, 50) local tg = {type="ball", range=self:getTalentRange(t), radius=radius, friendlyfire=self:spellFriendlyFire()} local x, y = self:getTarget(tg) if not x or not y then return nil end local _ _, x, y = self:canProject(tg, x, y) - local grids = self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(5 + self:combatSpellpower(0.22) * self:getTalentLevel(t)), {type="shadow"}) + local grids = self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(self:combatTalentSpellDamage(t, 5, 120)), {type="shadow"}) -- Add a lasting map effect game.level.map:addEffect(self, x, y, duration, @@ -84,8 +84,8 @@ newTalent{ return ([[Invokes a blast of shadows dealing %0.2f darkness damage and leaving a field that does %0.2f darkness damage per turn for %d turns.. The damage will increase with the Magic stat]]): format( - 5 + self:combatSpellpower(0.22) * self:getTalentLevel(t), - 4 + self:combatSpellpower(0.12) * self:getTalentLevel(t), + self:combatTalentSpellDamage(t, 5, 120), + self:combatTalentSpellDamage(t, 4, 50), self:getTalentLevel(t) + 2 ) end, @@ -117,8 +117,8 @@ newTalent{ It also regenerates both your negative and positive energies. The damage will increase with the Magic stat]]): format( - 10 + self:combatSpellpower(0.2) * self:getTalentLevel(t), - 10 + self:combatSpellpower(0.2) * self:getTalentLevel(t), + self:combatTalentSpellDamage(t, 10, 100), + self:combatTalentSpellDamage(t, 10, 100), self:getTalentRange(t) ) end, @@ -139,7 +139,7 @@ newTalent{ local tg = {type="ball", range=self:getTalentRange(t), radius=1 + math.floor(self:getTalentLevelRaw(t) / 3), friendlyfire=self:spellFriendlyFire(), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - local grids = self:project(tg, x, y, DamageType.DARKSTUN, self:spellCrit(28 + self:combatSpellpower(0.5) * self:getTalentLevel(t))) + local grids = self:project(tg, x, y, DamageType.DARKSTUN, self:spellCrit(self:combatTalentSpellDamage(t, 28, 170))) local _ _, x, y = self:canProject(tg, x, y) game.level.map:particleEmitter(x, y, tg.radius, "shadow_flash", {radius=tg.radius, grids=grids, tx=x, ty=y}) @@ -148,6 +148,6 @@ newTalent{ end, info = function(self, t) return ([[A star falls onto the target, stunning all and doing %0.2f darkness damage. - The damage will increase with the Magic stat]]):format(self:getTalentLevel(t), 28 + self:combatSpellpower(0.5) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 28, 170)) end, } diff --git a/game/modules/tome/data/talents/divine/sun.lua b/game/modules/tome/data/talents/divine/sun.lua index 5de562b73f..f7d3f9f762 100644 --- a/game/modules/tome/data/talents/divine/sun.lua +++ b/game/modules/tome/data/talents/divine/sun.lua @@ -33,13 +33,13 @@ newTalent{ local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.LIGHT, self:spellCrit(6 + self:combatSpellpower(0.3) * self:getTalentLevel(t)), {type="light"}) + self:project(tg, x, y, DamageType.LIGHT, self:spellCrit(self:combatTalentSpellDamage(t, 6, 160)), {type="light"}) local _ _, x, y = self:canProject(tg, x, y) -- Add a lasting map effect game.level.map:addEffect(self, x, y, 4, - DamageType.LIGHT, 6 + self:combatSpellpower(0.3) * self:getTalentLevel(t), + DamageType.LIGHT, self:combatTalentSpellDamage(t, 6, 80), 0, 5, nil, {type="light_zone"}, @@ -51,7 +51,7 @@ newTalent{ end, info = function(self, t) return ([[Calls the power of the Sun into a searing light doing %0.2f damage and leaving a spot on the ground for 4 turns doing %0.2f damage. - The damage will increase with the Magic stat]]):format(6 + self:combatSpellpower(0.3) * self:getTalentLevel(t), 6 + self:combatSpellpower(0.3) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 6, 160), self:combatTalentSpellDamage(t, 6, 80)) end, } @@ -72,7 +72,7 @@ newTalent{ tg.friendlyfire = false local grids = self:project(tg, self.x, self.y, DamageType.BLIND, 3 + self:getTalentLevel(t)) if self:getTalentLevel(t) then - self:project(tg, self.x, self.y, DamageType.LIGHT, 4 + self:combatSpellpower(0.15) * self:getTalentLevel(t)) + self:project(tg, self.x, self.y, DamageType.LIGHT, self:combatTalentSpellDamage(t, 4, 80)) end game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y, max_alpha=80}) game:playSoundNear(self, "talents/flame") @@ -83,7 +83,7 @@ newTalent{ At level 3 it will start dealing %0.2f light damage. The damage will increase with the Magic stat]]): format( - 4 + self:combatSpellpower(0.15) * self:getTalentLevel(t) + self:combatTalentSpellDamage(t, 4, 80) ) end, } @@ -103,7 +103,7 @@ newTalent{ local tg = {type="beam", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.FIRE, self:spellCrit(10 + self:combatSpellpower(0.4) * self:getTalentLevel(t))) + self:project(tg, x, y, DamageType.FIRE, self:spellCrit(self:combatTalentSpellDamage(t, 10, 200))) local _ _, x, y = self:canProject(tg, x, y) game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "light_beam", {tx=x-self.x, ty=y-self.y}) @@ -113,7 +113,7 @@ newTalent{ info = function(self, t) return ([[Fire a beam of sun flames at your foes, burning all those in line for %0.2f fire damage. The damage will increase with the Magic stat]]): - format(10 + self:combatSpellpower(0.4) * self:getTalentLevel(t)) + format(self:combatTalentSpellDamage(t, 10, 200)) end, } @@ -130,7 +130,7 @@ newTalent{ range = 3, action = function(self, t) local tg = {type="ball", range=0, radius=3, friendlyfire=false, talent=t} - local grids = self:project(tg, self.x, self.y, DamageType.LIGHT, self:spellCrit(10 + self:combatSpellpower(0.27) * self:getTalentLevel(t))) + local grids = self:project(tg, self.x, self.y, DamageType.LIGHT, self:spellCrit(self:combatTalentSpellDamage(t, 10, 160))) game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y}) @@ -139,6 +139,6 @@ newTalent{ end, info = function(self, t) return ([[Conjures a furious burst of sunlight, dealing %0.2f light damage to all those around you in a radius of 4. - The damage will increase with the Magic stat]]):format(self:getTalentLevel(t), 10 + self:combatSpellpower(0.27) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:getTalentLevel(t), self:combatTalentSpellDamage(t, 10, 160)) end, } diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua index dda45913b7..d09ede3114 100644 --- a/game/modules/tome/data/talents/misc/npcs.lua +++ b/game/modules/tome/data/talents/misc/npcs.lua @@ -90,7 +90,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end self.combat_apr = self.combat_apr + 1000 - self:attackTarget(target, DamageType.ACID, 1 + self:getTalentLevel(t) / 3, true) + self:attackTarget(target, DamageType.ACID, self:combatTalentWeaponDamage(t, 1, 1.8), true) self.combat_apr = self.combat_apr - 1000 return true end, @@ -113,7 +113,7 @@ newTalent{ if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end self.combat_apr = self.combat_apr + 1000 - self:attackTarget(target, DamageType.BLIND, 0.8 + self:getTalentLevel(t) / 10, true) + self:attackTarget(target, DamageType.BLIND, self:combatTalentWeaponDamage(t, 0.8, 1.4), true) self.combat_apr = self.combat_apr - 1000 return true end, @@ -156,7 +156,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) -- Try to stun ! if hit then @@ -170,7 +170,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage, if the attack hits, the target is stunned.]]):format(100 * (0.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target doing %d%% damage, if the attack hits, the target is stunned.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1)) end, } @@ -186,7 +186,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) -- Try to stun ! if hit then @@ -200,7 +200,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage, if the attack hits, the target is constricted.]]):format(100 * (0.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target doing %d%% damage, if the attack hits, the target is constricted.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1)) end, } @@ -216,7 +216,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 1.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1.5, 2), true) -- Try to knockback ! if hit then @@ -230,7 +230,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target is knocked back.]]):format(100 * (1.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target is knocked back.]]):format(100 * self:combatTalentWeaponDamage(t, 1.5, 2)) end, } @@ -309,7 +309,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) -- Try to rot ! if hit then @@ -323,7 +323,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * (0.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1)) end, } @@ -338,7 +338,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) -- Try to rot ! if hit then @@ -352,7 +352,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * (0.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1)) end, } @@ -367,7 +367,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.5 + self:getTalentLevel(t) / 10, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) -- Try to rot ! if hit then @@ -381,7 +381,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * (0.5 + self:getTalentLevel(t) / 10)) + return ([[Hits the target doing %d%% damage, if the attack hits, the target is diseased.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1)) end, } @@ -441,7 +441,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local hit = self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 7, true) + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.8, 1.4), true) -- Try to stun ! if hit then @@ -455,7 +455,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target doing %d%% damage; if the attack hits, the target is pinned to the ground.]]):format(100 * (0.8 + self:getTalentLevel(t) / 7)) + return ([[Hits the target doing %d%% damage; if the attack hits, the target is pinned to the ground.]]):format(100 * self:combatTalentWeaponDamage(t, 0.8, 1.4)) end, } diff --git a/game/modules/tome/data/talents/spells/arcane.lua b/game/modules/tome/data/talents/spells/arcane.lua index 81d40a22e6..048477be27 100644 --- a/game/modules/tome/data/talents/spells/arcane.lua +++ b/game/modules/tome/data/talents/spells/arcane.lua @@ -25,8 +25,9 @@ newTalent{ sustain_mana = 50, points = 5, cooldown = 30, + spellpower_increase = { 5, 9, 13, 16, 18 }, activate = function(self, t) - local power = 5 * self:getTalentLevelRaw(t) + local power = t.spellpower_increase[self:getTalentLevelRaw(t)] game:playSoundNear(self, "talents/arcane") return { power = self:addTemporaryValue("combat_spellpower", power), @@ -39,7 +40,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Your mastery of magic allows you to enter a deep concentration state, increasing your spellpower by %d.]]):format(5 * self:getTalentLevelRaw(t)) + return ([[Your mastery of magic allows you to enter a deep concentration state, increasing your spellpower by %d.]]):format(t.spellpower_increase[self:getTalentLevelRaw(t)]) end, } @@ -60,7 +61,7 @@ newTalent{ if self:getTalentLevel(t) >= 3 then tg.type = "beam" end local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.ARCANE, self:spellCrit(20 + self:combatSpellpower(0.5) * self:getTalentLevel(t)), nil) + self:project(tg, x, y, DamageType.ARCANE, self:spellCrit(self:combatTalentSpellDamage(t, 20, 230)), nil) local _ _, x, y = self:canProject(tg, x, y) if tg.type == "beam" then game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "mana_beam", {tx=x-self.x, ty=y-self.y}) @@ -73,7 +74,7 @@ newTalent{ info = function(self, t) return ([[Conjures up mana into a powerful bolt doing %0.2f arcane damage. At level 3 it becomes a beam. - The damage will increase with the Magic stat]]):format(20 + self:combatSpellpower(0.5) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 20, 230)) end, } @@ -89,14 +90,14 @@ newTalent{ }, action = function(self, t) if not self:hasEffect(self.EFF_MANAFLOW) then - self:setEffect(self.EFF_MANAFLOW, 10, {power=5+self:combatSpellpower(0.06) * self:getTalentLevel(t)}) + self:setEffect(self.EFF_MANAFLOW, 10, {power=self:combatTalentSpellDamage(t, 10, 20)}) game:playSoundNear(self, "talents/arcane") end return true end, info = function(self, t) return ([[Engulf yourself in a surge of mana, quickly restoring %d mana every turns for 10 turns. - The mana restored will increase with the Magic stat]]):format(5 + self:combatSpellpower(0.06) * self:getTalentLevel(t)) + The mana restored will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 20)) end, } diff --git a/game/modules/tome/data/talents/spells/conveyance.lua b/game/modules/tome/data/talents/spells/conveyance.lua index 39e72be7bf..e15adc02a4 100644 --- a/game/modules/tome/data/talents/spells/conveyance.lua +++ b/game/modules/tome/data/talents/spells/conveyance.lua @@ -127,7 +127,7 @@ newTalent{ if not tx or not ty or not target then return nil end local dur = util.bound(10 + math.floor(self:getTalentLevel(t) * 3), 10, 25) - local power = 50 + self:combatSpellpower(0.4) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 20, 210) local chance = 20 + self:getTalentLevel(t) * 5 self:setEffect(self.EFF_DISPLACEMENT_SHIELD, dur, {power=power, target=target, chance=chance}) game:playSoundNear(self, "talents/teleport") @@ -137,7 +137,7 @@ newTalent{ return ([[This intricate spell erects a space distortion around the caster that is linked to another one around a target. Any time the caster should take damage there is a %d%% chance that it will instead be warped by the shield and hit the designated target. Once the maximum damage (%d) is absorbed, the time runs out (%d turns), or the target dies, the shield will crumble. - The duration and max absorption will increase with the Magic stat]]):format(20 + self:getTalentLevel(t) * 5, 50 + self:combatSpellpower(0.4) * self:getTalentLevel(t), util.bound(10 + math.floor(self:getTalentLevel(t) * 3), 10, 25)) + The duration and max absorption will increase with the Magic stat]]):format(20 + self:getTalentLevel(t) * 5, self:combatTalentSpellDamage(t, 20, 210), util.bound(10 + math.floor(self:getTalentLevel(t) * 3), 10, 25)) end, } diff --git a/game/modules/tome/data/talents/spells/earth.lua b/game/modules/tome/data/talents/spells/earth.lua index 43ac51ba69..8fd78eb28e 100644 --- a/game/modules/tome/data/talents/spells/earth.lua +++ b/game/modules/tome/data/talents/spells/earth.lua @@ -32,7 +32,7 @@ newTalent{ }, activate = function(self, t) game:playSoundNear(self, "talents/earth") - local power = 4 + self:combatSpellpower(0.03) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 10, 20) return { armor = self:addTemporaryValue("combat_armor", power), particle = self:addParticles(Particles.new("stone_skin", 1)), @@ -45,7 +45,7 @@ newTalent{ end, info = function(self, t) return ([[The caster's skin grows as hard as stone, granting %d bonus to armor. - The bonus to armor will increase with the Magic stat]]):format(4 + self:combatSpellpower(0.03) * self:getTalentLevel(t)) + The bonus to armor will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 20)) end, } @@ -88,7 +88,7 @@ newTalent{ local tg = {type="bolt", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.SPELLKNOCKBACK, self:spellCrit(8 + self:combatSpellpower(0.15) * self:getTalentLevel(t))) + self:project(tg, x, y, DamageType.SPELLKNOCKBACK, self:spellCrit(self:combatTalentSpellDamage(t, 8, 170))) game:playSoundNear(self, "talents/earth") return true end, diff --git a/game/modules/tome/data/talents/spells/enhancement.lua b/game/modules/tome/data/talents/spells/enhancement.lua index 2007cf56df..eff5d11134 100644 --- a/game/modules/tome/data/talents/spells/enhancement.lua +++ b/game/modules/tome/data/talents/spells/enhancement.lua @@ -31,8 +31,8 @@ newTalent{ activate = function(self, t) game:playSoundNear(self, "talents/fire") return { - dam = self:addTemporaryValue("melee_project", {[DamageType.FIRE] = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.08)}), - per = self:addTemporaryValue("inc_damage", {[DamageType.FIRE] = 5 + self:getTalentLevel(t) * self:combatSpellpower(0.05)}), + dam = self:addTemporaryValue("melee_project", {[DamageType.FIRE] = self:combatTalentSpellDamage(t, 5, 20)}), + per = self:addTemporaryValue("inc_damage", {[DamageType.FIRE] = self:combatTalentSpellDamage(t, 5, 14)}), } end, deactivate = function(self, t, p) @@ -42,7 +42,7 @@ newTalent{ end, info = function(self, t) return ([[Engulfs your hands (and weapons) in a sheath of fire, dealing %d fire damage per melee attack and increasing all fire damage by %d%%.]]): - format(5 + self:getTalentLevel(t) * self:combatSpellpower(0.08), 5 + self:getTalentLevel(t) * self:combatSpellpower(0.05)) + format(self:combatTalentSpellDamage(t, 5, 20), self:combatTalentSpellDamage(t, 5, 14)) end, } @@ -56,11 +56,11 @@ newTalent{ range = 20, action = function(self, t) game:playSoundNear(self, "talents/spell_generic") - self:setEffect(self.EFF_EARTHEN_BARRIER, 10, {power=10 + self:getTalentLevel(t) * self:combatSpellpower(0.08)}) + self:setEffect(self.EFF_EARTHEN_BARRIER, 10, {power=self:combatTalentSpellDamage(t, 10, 60)}) return true end, info = function(self, t) - return ([[Hardens your skin with the power of earth, reducing physical damage taken by %d%%.]]):format(10 + self:getTalentLevel(t) * self:combatSpellpower(0.08)) + return ([[Hardens your skin with the power of earth, reducing physical damage taken by %d%%.]]):format(self:combatTalentSpellDamage(t, 10, 60)) end, } @@ -78,8 +78,8 @@ newTalent{ activate = function(self, t) game:playSoundNear(self, "talents/ice") return { - dam = self:addTemporaryValue("melee_project", {[DamageType.ICE] = 3 + self:getTalentLevel(t) * self:combatSpellpower(0.05)}), - per = self:addTemporaryValue("inc_damage", {[DamageType.COLD] = 4 + self:getTalentLevel(t) * self:combatSpellpower(0.04)}), + dam = self:addTemporaryValue("melee_project", {[DamageType.ICE] = self:combatTalentSpellDamage(t, 3, 15)}), + per = self:addTemporaryValue("inc_damage", {[DamageType.COLD] = self:combatTalentSpellDamage(t, 5, 14)}), } end, deactivate = function(self, t, p) @@ -89,7 +89,7 @@ newTalent{ end, info = function(self, t) return ([[Engulfs your hands (and weapons) in a sheath of ice, dealing %d ice damage per melee attack and increasing all cold damage by %d%%.]]): - format(3 + self:getTalentLevel(t) * self:combatSpellpower(0.05), 4 + self:getTalentLevel(t) * self:combatSpellpower(0.04)) + format(self:combatTalentSpellDamage(t, 3, 15), self:combatTalentSpellDamage(t, 5, 14)) end, } @@ -106,7 +106,7 @@ newTalent{ }, activate = function(self, t) game:playSoundNear(self, "talents/spell_generic") - local power = math.floor(2 + self:getTalentLevel(t) * self:combatSpellpower(0.06)) + local power = math.floor(self:combatTalentSpellDamage(t, 2, 18)) return { stats = self:addTemporaryValue("inc_stats", { [self.STAT_STR] = power, @@ -124,6 +124,6 @@ newTalent{ end, info = function(self, t) return ([[You concentrate on your inner self, increasing your stats each by %d.]]): - format(2 + self:getTalentLevel(t) * self:combatSpellpower(0.06)) + format(self:combatTalentSpellDamage(t, 2, 18)) end, } diff --git a/game/modules/tome/data/talents/spells/nature.lua b/game/modules/tome/data/talents/spells/nature.lua index 76e8b0af22..5cbba02616 100644 --- a/game/modules/tome/data/talents/spells/nature.lua +++ b/game/modules/tome/data/talents/spells/nature.lua @@ -28,13 +28,13 @@ newTalent{ HEAL = 10, }, action = function(self, t) - self:setEffect(self.EFF_REGENERATION, 10, {power=5 + self:combatSpellpower(0.07) * self:getTalentLevel(t)}) + self:setEffect(self.EFF_REGENERATION, 10, {power=self:combatTalentSpellDamage(t, 5, 25)}) game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) return ([[Call upon the forces of nature to regenerate your body for %d life every turn for 10 turns. - The life healed will increase with the Magic stat]]):format(5 + self:combatSpellpower(0.07) * self:getTalentLevel(t)) + The life healed will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 25)) end, } @@ -49,13 +49,13 @@ newTalent{ HEAL = 10, }, action = function(self, t) - self:heal(self:spellCrit(10 + self:combatSpellpower(0.5) * self:getTalentLevel(t)), self) + self:heal(self:spellCrit(self:combatTalentSpellDamage(t, 40, 220)), self) game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) return ([[Call upon the forces of nature to heal your body for %d life. - The life healed will increase with the Magic stat]]):format(10 + self:combatSpellpower(0.5) * self:getTalentLevel(t)) + The life healed will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 40, 220)) end, } diff --git a/game/modules/tome/data/talents/spells/phantasm.lua b/game/modules/tome/data/talents/spells/phantasm.lua index 7164e6504b..da029debc5 100644 --- a/game/modules/tome/data/talents/spells/phantasm.lua +++ b/game/modules/tome/data/talents/spells/phantasm.lua @@ -53,7 +53,7 @@ newTalent{ DEFEND = 10, }, activate = function(self, t) - local power = 4 + self:combatSpellpower(0.04) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 4, 30) game:playSoundNear(self, "talents/heal") return { particle = self:addParticles(Particles.new("phantasm_shield", 1)), @@ -67,7 +67,7 @@ newTalent{ end, info = function(self, t) return ([[The caster's image blurs, making them harder to hit, granting %d bonus to defense. - The bonus will increase with the Magic stat]]):format(4 + self:combatSpellpower(0.04) * self:getTalentLevel(t)) + The bonus will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 4, 30)) end, } @@ -83,7 +83,7 @@ newTalent{ DEFEND = 10, }, activate = function(self, t) - local power = 10 + self:combatSpellpower(0.06) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 10, 170) game:playSoundNear(self, "talents/heal") return { particle = self:addParticles(Particles.new("phantasm_shield", 1)), @@ -97,7 +97,7 @@ newTalent{ end, info = function(self, t) return ([[The caster surrounds themselves with a phantasmal shield. If hit in melee the shield will deal %d arcane damage to the attacker. - The damage will increase with the Magic stat]]):format(10 + self:combatSpellpower(0.06) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 170)) end, } @@ -113,7 +113,7 @@ newTalent{ DEFEND = 10, }, activate = function(self, t) - local power = 4 + self:combatSpellpower(0.04) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 10, 30) game:playSoundNear(self, "talents/heal") return { invisible = self:addTemporaryValue("invisible", power), @@ -129,6 +129,6 @@ newTalent{ return ([[The caster fades from sight, granting %d bonus to invisibility. Beware, you should take off your light, otherwise you will still be easily spotted. This powerful spell constantly drains your mana while active. - The bonus will increase with the Magic stat]]):format(4 + self:combatSpellpower(0.04) * self:getTalentLevel(t)) + The bonus will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 10, 30)) end, } diff --git a/game/modules/tome/data/talents/spells/temporal.lua b/game/modules/tome/data/talents/spells/temporal.lua index f282343d6b..62f8233236 100644 --- a/game/modules/tome/data/talents/spells/temporal.lua +++ b/game/modules/tome/data/talents/spells/temporal.lua @@ -109,7 +109,7 @@ newTalent{ range = 20, action = function(self, t) local dur = util.bound(5 + math.floor(self:getTalentLevel(t)), 5, 15) - local power = 50 + self:combatSpellpower(0.5) * self:getTalentLevel(t) + local power = self:combatTalentSpellDamage(t, 50, 170) self:setEffect(self.EFF_TIME_SHIELD, dur, {power=power}) game:playSoundNear(self, "talents/spell_generic") return true @@ -117,6 +117,6 @@ newTalent{ info = function(self, t) return ([[This intricate spell erects a time shield around the caster, preventing any incoming damage and sending it forward in time. Once either the maximum damage (%d) is absorbed, or the time runs out (%d turns), the stored damage will return as self-damage over time (5 turns). - The duration and max absorption will increase with the Magic stat]]):format(50 + self:combatSpellpower(0.5) * self:getTalentLevel(t), util.bound(5 + math.floor(self:getTalentLevel(t)), 5, 15)) + The duration and max absorption will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 50, 170), util.bound(5 + math.floor(self:getTalentLevel(t)), 5, 15)) end, } diff --git a/game/modules/tome/data/talents/spells/water.lua b/game/modules/tome/data/talents/spells/water.lua index 9389addf44..dca1271ca4 100644 --- a/game/modules/tome/data/talents/spells/water.lua +++ b/game/modules/tome/data/talents/spells/water.lua @@ -31,7 +31,7 @@ newTalent{ action = function(self, t) local duration = self:getTalentLevel(t) + 2 local radius = 3 - local dam = 4 + self:combatSpellpower(0.17) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 4, 70) local tg = {type="ball", range=self:getTalentRange(t), radius=radius} local x, y = self:getTarget(tg) if not x or not y then return nil end @@ -50,7 +50,7 @@ newTalent{ end, info = function(self, t) return ([[Corrosive fumes rises from the ground doing %0.2f acid damage in a radius of 3 each turn for %d turns. - The damage and duration will increase with the Magic stat]]):format(4 + self:combatSpellpower(0.17) * self:getTalentLevel(t), self:getTalentLevel(t) + 2) + The damage and duration will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 4, 70), self:getTalentLevel(t) + 2) end, } @@ -70,14 +70,14 @@ newTalent{ local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.COLD, self:spellCrit(12 + self:combatSpellpower(0.25) * self:getTalentLevel(t)), {type="freeze"}) + self:project(tg, x, y, DamageType.COLD, self:spellCrit(self:combatTalentSpellDamage(t, 12, 160)), {type="freeze"}) self:project(tg, x, y, DamageType.FREEZE, 3 + math.floor(self:getTalentLevel(t) / 3)) game:playSoundNear(self, "talents/ice") return true end, info = function(self, t) return ([[Condenses ambient water on a target, freezing it for a short while and damaging it for %0.2f. - The damage will increase with the Magic stat]]):format(12 + self:combatSpellpower(0.25) * self:getTalentLevel(t)) + The damage will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 12, 160)) end, } @@ -94,7 +94,7 @@ newTalent{ action = function(self, t) local duration = 5 + self:combatSpellpower(0.01) * self:getTalentLevel(t) local radius = 1 - local dam = 5 + self:combatSpellpower(0.2) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 5, 90) -- Add a lasting map effect game.level.map:addEffect(self, self.x, self.y, duration, @@ -112,7 +112,7 @@ newTalent{ end, info = function(self, t) return ([[A wall of water rushes out from the caster doing %0.2f cold damage and knocking back targets each turn for %d turns. - The damage and duration will increase with the Magic stat]]):format(5 + self:combatSpellpower(0.2) * self:getTalentLevel(t), 5 + self:combatSpellpower(0.01) * self:getTalentLevel(t)) + The damage and duration will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 90), 5 + self:combatSpellpower(0.01) * self:getTalentLevel(t)) end, } @@ -129,7 +129,7 @@ newTalent{ action = function(self, t) local duration = 5 + self:combatSpellpower(0.05) + self:getTalentLevel(t) local radius = 3 - local dam = 5 + self:combatSpellpower(0.15) * self:getTalentLevel(t) + local dam = self:combatTalentSpellDamage(t, 5, 90) -- Add a lasting map effect game.level.map:addEffect(self, self.x, self.y, duration, @@ -150,6 +150,6 @@ newTalent{ info = function(self, t) return ([[A furious ice storm rages around the caster doing %0.2f cold damage in a radius of 3 each turn for %d turns. It has 25%% chance to freeze damaged targets. - The damage and duration will increase with the Magic stat]]):format(5 + self:combatSpellpower(0.15) * self:getTalentLevel(t), 5 + self:combatSpellpower(0.05) + self:getTalentLevel(t)) + The damage and duration will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 5, 90), 5 + self:combatSpellpower(0.05) + self:getTalentLevel(t)) end, } diff --git a/game/modules/tome/data/talents/techniques/2hweapon.lua b/game/modules/tome/data/talents/techniques/2hweapon.lua index 161af8bc15..a12a843967 100644 --- a/game/modules/tome/data/talents/techniques/2hweapon.lua +++ b/game/modules/tome/data/talents/techniques/2hweapon.lua @@ -71,14 +71,14 @@ newTalent{ local x, y = self.x + i, self.y + j if (self.x ~= x or self.y ~= y) and game.level.map:isBound(x, y) and game.level.map(x, y, Map.ACTOR) then local target = game.level.map(x, y, Map.ACTOR) - self:attackTargetWith(target, weapon.combat, nil, 1.4 + self:getTalentLevel(t) / 8) + self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 1.4, 2.1)) end end end return true end, info = function(self, t) - return ([[Spin around, extending your weapon and damaging all targets around for %d%% weapon damage.]]):format(100 * (1.4 + self:getTalentLevel(t) / 8)) + return ([[Spin around, extending your weapon and damaging all targets around for %d%% weapon damage.]]):format(100 * self:combatTalentWeaponDamage(t, 1.4, 2.1)) end, } @@ -142,7 +142,7 @@ newTalent{ end self.combat_physcrit = self.combat_physcrit + 100 - local speed, hit = self:attackTargetWith(target, weapon.combat, nil, 1.4 + self:getTalentLevel(t) / 6) + local speed, hit = self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 0.8, 1.3)) if self:getTalentLevel(t) >= 4 then self.combat_dam = self.combat_dam - inc @@ -164,7 +164,7 @@ newTalent{ end, info = function(self, t) return ([[Tries to perform a killing blow doing %d%% weapon damage, granting an automatic critical hit. If the target ends up with low enough life it might be instantly killed. - At level 4 it drains all remaining stamina and uses it to increase the blow damage.]]):format(100 * (1.4 + self:getTalentLevel(t) / 6)) + At level 4 it drains all remaining stamina and uses it to increase the blow damage.]]):format(100 * self:combatTalentWeaponDamage(t, 0.8, 1.3)) end, } @@ -189,7 +189,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local speed, hit = self:attackTargetWith(target, weapon.combat, nil, 1.2 + self:getTalentLevel(t) / 10) + local speed, hit = self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 1, 1.5)) -- Try to stun ! if hit then @@ -203,7 +203,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target is stunned.]]):format(100 * (1.2 + self:getTalentLevel(t) / 10)) + return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target is stunned.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5)) end, } @@ -225,7 +225,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local speed, hit = self:attackTargetWith(target, weapon.combat, nil, 1.5 + self:getTalentLevel(t) / 10) + local speed, hit = self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 1, 1.5)) -- Try to stun ! if hit then @@ -239,7 +239,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target's armour is reduced by %d.]]):format(100 * (1.5 + self:getTalentLevel(t) / 10), 5*self:getTalentLevel(t)) + return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target's armour is reduced by %d.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5), 5*self:getTalentLevel(t)) end, } @@ -261,7 +261,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local speed, hit = self:attackTargetWith(target, weapon.combat, nil, 1.5 + self:getTalentLevel(t) / 10) + local speed, hit = self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 1, 1.5)) -- Try to stun ! if hit then @@ -275,7 +275,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target's attack power is reduced by %d.]]):format(100 * (1.5 + self:getTalentLevel(t) / 10), 3*self:getTalentLevel(t)) + return ([[Hits the target with your weapon doing %d%% damage, if the attack hits, the target's attack power is reduced by %d.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5), 3*self:getTalentLevel(t)) end, } @@ -297,7 +297,7 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - local speed, hit = self:attackTargetWith(target, weapon.combat, nil, 1.4 + self:getTalentLevel(t) / 10) + local speed, hit = self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 1, 1.4)) -- Try to stun ! if hit then @@ -311,6 +311,6 @@ newTalent{ return true end, info = function(self, t) - return ([[Hits the target with a mighty blow to the legs doing %d%% weapon damage, if the attack hits, the target is unable to move for %d turns.]]):format(100 * (1.4 + self:getTalentLevel(t) / 10), 2+self:getTalentLevel(t)) + return ([[Hits the target with a mighty blow to the legs doing %d%% weapon damage, if the attack hits, the target is unable to move for %d turns.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.4), 2+self:getTalentLevel(t)) end, } diff --git a/game/modules/tome/data/talents/techniques/archery.lua b/game/modules/tome/data/talents/techniques/archery.lua index c2eb1f8bd0..7ce5eb67c7 100644 --- a/game/modules/tome/data/talents/techniques/archery.lua +++ b/game/modules/tome/data/talents/techniques/archery.lua @@ -47,12 +47,12 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 5) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.1, 2.2)) print(energy , self.energy.value, energy ~= self.energy.value) return energy ~= self.energy.value end, info = function(self, t) - return ([[A steady shot, doing %d%% damage.]]):format((1.2 + self:getTalentLevel(t) / 5) * 100) + return ([[A steady shot, doing %d%% damage.]]):format(self:combatTalentWeaponDamage(t, 1.1, 2.2) * 100) end, } @@ -144,12 +144,12 @@ newTalent{ action = function(self, t) local energy = self.energy.value self.combat_physcrit = self.combat_physcrit + 1000 - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 4) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.2, 2)) self.combat_physcrit = self.combat_physcrit - 1000 return energy ~= self.energy.value end, info = function(self, t) - return ([[You concentrate on your aim to produce a guaranted critical hit (with a base damage of %d%%).]]):format((1.2 + self:getTalentLevel(t) / 4) * 100) + return ([[You concentrate on your aim to produce a guaranted critical hit (with a base damage of %d%%).]]):format(self:combatTalentWeaponDamage(t, 1.2, 2) * 100) end, } @@ -202,7 +202,7 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1 + self:getTalentLevel(t) / 6, function(target, x, y) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then target:setEffect(target.EFF_SLOW, 7, {power=util.bound((self:combatAttack() * 0.15 * self:getTalentLevel(t)) / 100, 0.1, 0.4)}) else @@ -212,7 +212,7 @@ newTalent{ return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire a crippling shot, doing %d%% damage and reducing your target's speed by %0.2f for 7 turns.]]):format((1 + self:getTalentLevel(t) / 6) * 100, util.bound((5 + 5 * self:getTalentLevel(t)) / 100, 0.1, 0.4)) + return ([[You fire a crippling shot, doing %d%% damage and reducing your target's speed by %0.2f for 7 turns.]]):format(self:combatTalentWeaponDamage(t, 1, 1.5) * 100, util.bound((5 + 5 * self:getTalentLevel(t)) / 100, 0.1, 0.4)) end, } @@ -227,7 +227,7 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1 + self:getTalentLevel(t) / 6, function(target, x, y) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.4), function(target, x, y) if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then target:setEffect(target.EFF_PINNED, 2 + self:getTalentLevelRaw(t), {}) else @@ -237,7 +237,7 @@ newTalent{ return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire a pinning shot, doing %d%% damage and pinning your target to the ground for %d turns.]]):format((1 + self:getTalentLevel(t) / 6) * 100, 2 + self:getTalentLevelRaw(t)) + return ([[You fire a pinning shot, doing %d%% damage and pinning your target to the ground for %d turns.]]):format(self:combatTalentWeaponDamage(t, 1, 1.4) * 100, 2 + self:getTalentLevelRaw(t)) end, } @@ -252,7 +252,7 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1 + self:getTalentLevel(t) / 6, function(target, x, y) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.5, 1.5), function(target, x, y) if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then target:setEffect(target.EFF_STUNNED, 2 + self:getTalentLevelRaw(t), {}) else @@ -262,6 +262,6 @@ newTalent{ return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire multiple shots at the area, doing %d%% damage and stunning your targets for %d turns.]]):format((0.5 + self:getTalentLevel(t) / 6) * 100, 2 + self:getTalentLevelRaw(t)) + return ([[You fire multiple shots at the area, doing %d%% damage and stunning your targets for %d turns.]]):format(self:combatTalentWeaponDamage(t, 0.5, 1.5), 2 + self:getTalentLevelRaw(t)) end, } diff --git a/game/modules/tome/data/talents/techniques/bow.lua b/game/modules/tome/data/talents/techniques/bow.lua index ffb4becfd4..047c7b31fd 100644 --- a/game/modules/tome/data/talents/techniques/bow.lua +++ b/game/modules/tome/data/talents/techniques/bow.lua @@ -40,12 +40,12 @@ newTalent{ action = function(self, t) local energy = self.energy.value self.combat_apr = self.combat_apr + 1000 - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 7, nil, {type="beam"}, {one_shot=true}) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), nil, {type="beam"}, {one_shot=true}) self.combat_apr = self.combat_apr - 1000 return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire an arrow that cuts right through anything, piercing multiple targets if possible with nigh infinite armor penetration, doing %d%% damage.]]):format(100 * (1.2 + self:getTalentLevel(t) / 7)) + return ([[You fire an arrow that cuts right through anything, piercing multiple targets if possible with nigh infinite armor penetration, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5)) end, } @@ -60,11 +60,11 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 5, nil, {type="ball", radius=1}, {limit_shots=2}) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.2, 1.7), nil, {type="ball", radius=1}, {limit_shots=2}) return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire two arrows at your target, hitting it and a nearby foe if possible, doing %d%% damage.]]):format(100 * (1.2 + self:getTalentLevel(t) / 5)) + return ([[You fire two arrows at your target, hitting it and a nearby foe if possible, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 1.7)) end, } @@ -79,10 +79,10 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 0.7 + self:getTalentLevel(t) / 5, nil, {type="ball", radius=2 + self:getTalentLevel(t)/3, firendlyfire=false}) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.6, 1.3), nil, {type="ball", radius=2 + self:getTalentLevel(t)/3, firendlyfire=false}) return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire multiple arrows at the area, doing %d%% damage.]]):format(100 * (0.7 + self:getTalentLevel(t) / 5)) + return ([[You fire multiple arrows at the area, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 0.6, 1.3)) end, } diff --git a/game/modules/tome/data/talents/techniques/dualweapon.lua b/game/modules/tome/data/talents/techniques/dualweapon.lua index da9b4bc119..9695a7abf2 100644 --- a/game/modules/tome/data/talents/techniques/dualweapon.lua +++ b/game/modules/tome/data/talents/techniques/dualweapon.lua @@ -122,7 +122,7 @@ newTalent{ if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end -- First attack with offhand - local speed, hit = self:attackTargetWith(target, offweapon.combat, nil, 1.2 + self:getTalentLevel(t) / 10) + local speed, hit = self:attackTargetWith(target, offweapon.combat, nil, self:combatTalentWeaponDamage(t, 0.7, 1.7)) -- Second attack with mainhand if hit then @@ -133,13 +133,13 @@ newTalent{ end -- Attack after the stun, to benefit from backstabs - self:attackTargetWith(target, weapon.combat, nil, 1.2 + self:getTalentLevel(t) / 10) + self:attackTargetWith(target, weapon.combat, nil, self:combatTalentWeaponDamage(t, 0.7, 1.7)) end return true end, info = function(self, t) - return ([[Hit with your offhand weapon for %d%% damage, if the attack hits, the target is stunned and you hit it with your mainhand weapon.]]):format(100 * (1.2 + self:getTalentLevel(t) / 10)) + return ([[Hit with your offhand weapon for %d%% damage, if the attack hits, the target is stunned and you hit it with your mainhand weapon.]]):format(100 * self:combatTalentWeaponDamage(t, 0.7, 1.7)) end, } @@ -161,14 +161,14 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 10, true) - self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 10, true) - self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 10, true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1.4), true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1.4), true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1.4), true) return true end, info = function(self, t) - return ([[Lashes out a flurry of blows, hitting your target three times with each weapon for %d%% damage.]]):format(100 * (0.8 + self:getTalentLevel(t) / 10)) + return ([[Lashes out a flurry of blows, hitting your target three times with each weapon for %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 0.5, 1.4)) end, } @@ -197,16 +197,16 @@ newTalent{ local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR) local hit - hit = self:attackTarget(target, nil, 1.2 + self:getTalentLevel(t) / 10, true) + hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true) if hit and target:canBe("cut") then target:setEffect(target.EFF_CUT, 3 + self:getTalentLevel(t), {power=self:getDex() * 0.5, src=self}) end if lt then - hit = self:attackTarget(lt, nil, 1.2 + self:getTalentLevel(t) / 10, true) + hit = self:attackTarget(lt, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true) if hit and lt:canBe("cut") then lt:setEffect(lt.EFF_CUT, 3 + self:getTalentLevel(t), {power=self:getDex() * 0.5, src=self}) end end if rt then - hit = self:attackTarget(rt, nil, 1.2 + self:getTalentLevel(t) / 10, true) + hit = self:attackTarget(rt, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true) if hit and rt:canBe("cut") then rt:setEffect(rt.EFF_CUT, 3 + self:getTalentLevel(t), {power=self:getDex() * 0.5, src=self}) end end print(x,y,target) @@ -217,7 +217,7 @@ newTalent{ end, info = function(self, t) return ([[Attack your foes in a frontal arc doing %d%% weapon damage and making your targets bleed for %d each turn for %d turns.]]): - format(100 * (1.2 + self:getTalentLevel(t) / 10), self:getDex() * 0.5, 3 + self:getTalentLevel(t)) + format(100 * self:combatTalentWeaponDamage(t, 1, 1.7), self:getDex() * 0.5, 3 + self:getTalentLevel(t)) end, } @@ -239,13 +239,13 @@ newTalent{ local x, y = self.x + i, self.y + j if (self.x ~= x or self.y ~= y) and game.level.map:isBound(x, y) and game.level.map(x, y, Map.ACTOR) then local target = game.level.map(x, y, Map.ACTOR) - self:attackTarget(target, nil, 1.4 + self:getTalentLevel(t) / 10, true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1.2, 1.9), true) end end end return true end, info = function(self, t) - return ([[Spin around, damaging all targets around with both weapons for %d%%.]]):format(100 * (1.4 + self:getTalentLevel(t) / 10)) + return ([[Spin around, damaging all targets around with both weapons for %d%%.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 1.9)) end, } diff --git a/game/modules/tome/data/talents/techniques/sling.lua b/game/modules/tome/data/talents/techniques/sling.lua index 5451b86e7c..d4f48f8876 100644 --- a/game/modules/tome/data/talents/techniques/sling.lua +++ b/game/modules/tome/data/talents/techniques/sling.lua @@ -39,7 +39,7 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 5, function(target, x, y) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("blind") then target:setEffect(target.EFF_BLINDED, 2 + self:getTalentLevelRaw(t), {}) else @@ -49,7 +49,7 @@ newTalent{ return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire a shot to your target's eyes, blinding it for %d turns and doing %d%% damage.]]):format(2 + self:getTalentLevelRaw(t), 100 * (1.2 + self:getTalentLevel(t) / 5)) + return ([[You fire a shot to your target's eyes, blinding it for %d turns and doing %d%% damage.]]):format(2 + self:getTalentLevelRaw(t), 100 * self:combatTalentWeaponDamage(t, 1, 1.5)) end, } @@ -64,7 +64,7 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 1.2 + self:getTalentLevel(t) / 5, function(target, x, y) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 15) and target:canBe("knockback") then target:knockback(self.x, self.y, 4) game.logSeen(target, "%s is knocked back!", target.name:capitalize()) @@ -75,7 +75,7 @@ newTalent{ return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire a mighty shot at your target doing %d%% damage and knocking it back.]]):format(100 * (1.2 + self:getTalentLevel(t) / 5)) + return ([[You fire a mighty shot at your target doing %d%% damage and knocking it back.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5)) end, } @@ -90,10 +90,10 @@ newTalent{ range = 20, action = function(self, t) local energy = self.energy.value - self:archeryShoot(nil, 0.7 + self:getTalentLevel(t) / 5, nil, nil, {multishots=2+self:getTalentLevelRaw(t)/2}) + self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.3, 0.7), nil, nil, {multishots=2+self:getTalentLevelRaw(t)/2}) return energy ~= self.energy.value end, info = function(self, t) - return ([[You fire %d shots at your target, doing %d%% damage with each shot.]]):format(2+self:getTalentLevelRaw(t)/2, 100 * (0.7 + self:getTalentLevel(t) / 5)) + return ([[You fire %d shots at your target, doing %d%% damage with each shot.]]):format(2+self:getTalentLevelRaw(t)/2, 100 * self:combatTalentWeaponDamage(t, 0.3, 0.7)) end, } diff --git a/game/modules/tome/data/talents/techniques/weaponshield.lua b/game/modules/tome/data/talents/techniques/weaponshield.lua index f5a48100d9..3594eef7e2 100644 --- a/game/modules/tome/data/talents/techniques/weaponshield.lua +++ b/game/modules/tome/data/talents/techniques/weaponshield.lua @@ -39,8 +39,8 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end - self:attackTargetWith(target, shield.special_combat, nil, 1.5 + self:getTalentLevel(t) / 5) - local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, 1.3 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 5) + self:attackTargetWith(target, shield.special_combat, nil, self:combatTalentWeaponDamage(t, 1, 1.7)) + local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, self:combatTalentWeaponDamage(t, 1.2, 2.1, self:getTalentLevel(self.T_SHIELD_EXPERTISE)) / 5) -- Try to stun ! if hit then @@ -55,7 +55,7 @@ newTalent{ end, info = function(self, t) return ([[Hits the target with two shield strikes, stunning it and doing %d%% shield damage. - The damage multiplier increases with your strength.]]):format(100 * (1.3 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 5)) + The damage multiplier increases with your strength.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 2.1, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) end, } @@ -90,11 +90,11 @@ newTalent{ if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end -- First attack with weapon - self:attackTarget(target, nil, 0.8 + self:getTalentLevel(t) / 10, true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.8, 1.3), true) -- Second attack with shield - self:attackTargetWith(target, shield.special_combat, nil, 0.8 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 10) + self:attackTargetWith(target, shield.special_combat, nil, self:combatTalentWeaponDamage(t, 0.8, 1.3, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) -- Third attack with shield - local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, 0.8 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE)) / 10) + local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, self:combatTalentWeaponDamage(t, 0.8, 1.3, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) -- Try to stun ! if hit then @@ -109,7 +109,7 @@ newTalent{ end, info = function(self, t) return ([[Hits the target with your weapon and two shield strikes doing %d%% damage, trying to overpower your target. - If the last attack hits, the target is knocked back.]]):format(100 * (0.8 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 10)) + If the last attack hits, the target is knocked back.]]):format(100 * self:combatTalentWeaponDamage(t, 0.8, 1.3, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) end, } @@ -133,13 +133,13 @@ newTalent{ if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end -- First attack with shield - local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, 1 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 10) + local speed, hit = self:attackTargetWith(target, shield.special_combat, nil, self:combatTalentWeaponDamage(t, 1, 1.5, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) -- Second & third attack with weapon if hit then self.combat_physcrit = self.combat_physcrit + 1000 - self:attackTarget(target, nil, 1 + self:getTalentLevel(t) / 10, true) - self:attackTarget(target, nil, 1 + self:getTalentLevel(t) / 10, true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.5), true) + self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.5), true) self.combat_physcrit = self.combat_physcrit - 1000 end @@ -147,7 +147,7 @@ newTalent{ end, info = function(self, t) return ([[Hits the target with your shield doing %d%% damage. If it hits, you follow up with two weapon strikes which are automatic critrical hits.]]): - format(100 * (1 + (self:getTalentLevel(t) + self:getTalentLevel(self.T_SHIELD_EXPERTISE) / 2) / 10)) + format(100 * self:combatTalentWeaponDamage(t, 1, 1.5, self:getTalentLevel(self.T_SHIELD_EXPERTISE))) end, } diff --git a/src/core_lua.c b/src/core_lua.c index 009e2fe471..b9429768db 100644 --- a/src/core_lua.c +++ b/src/core_lua.c @@ -1040,7 +1040,8 @@ static int sdl_texture_outline(lua_State *L) glTexCoord2f(0,1); glVertex3f(0, h, 0); glEnd(); - + // Unbind texture from FBO and then unbind FBO + CHECKGL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0)); CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); // Restore viewport CHECKGL(glPopAttrib()); diff --git a/src/sdnoise1234.c b/src/sdnoise1234.c new file mode 100644 index 0000000000..f95a8be8cf --- /dev/null +++ b/src/sdnoise1234.c @@ -0,0 +1,752 @@ +/* sdnoise1234, Simplex noise with true analytic + * derivative in 1D to 4D. + * + * Copyright © 2003-2008, Stefan Gustavson + * + * Contact: stefan.gustavson@gmail.com + * + * This library 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 2 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \brief C implementation file for Perlin simplex noise with analytic + derivative over 1, 2, 3 and 4 dimensions. + \author Stefan Gustavson (stefan.gustavson@gmail.com) + \author Charl van Deventer (landon.skyfire@gmail.com) + */ + +/* + * This is an implementation of Perlin "simplex noise" over one + * dimension (x), two dimensions (x,y), three dimensions (x,y,z) + * and four dimensions (x,y,z,w). The analytic derivative is + * returned, to make it possible to do lots of fun stuff like + * flow animations, curl noise, analytic antialiasing and such. + * + * Visually, this noise is exactly the same as the plain version of + * simplex noise provided in the file "snoise1234.c". It just returns + * all partial derivatives in addition to the scalar noise value. + * + */ + +/* + * 23 June 2010: Modified by Charl van Deventer to allow periodic arguments + * Note: It doesn't check for bounds over 255 (wont work) and might fail with + * negative coords. + */ + +#include <math.h> + +#include "sdnoise1234.h" /* We strictly don't need this, but play nice. */ + +#define FASTFLOOR(x) ( ((x)>0) ? ((int)x) : (((int)x)-1) ) + +/* Static data ---------------------- */ + +/* + * Permutation table. This is just a random jumble of all numbers 0-255, + * repeated twice to avoid wrapping the index at 255 for each lookup. + */ +unsigned char perm[512] = {151,160,137,91,90,15, +131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, +190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, +88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, +77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, +102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, +135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, +5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, +223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, +129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, +251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, +49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, +138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, +151,160,137,91,90,15, +131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, +190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, +88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, +77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, +102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, +135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, +5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, +223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, +129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, +251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, +49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, +138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 +}; + +/* + * Gradient tables. These could be programmed the Ken Perlin way with + * some clever bit-twiddling, but this is more clear, and not really slower. + */ +static float grad2lut[8][2] = { + { -1.0f, -1.0f }, { 1.0f, 0.0f } , { -1.0f, 0.0f } , { 1.0f, 1.0f } , + { -1.0f, 1.0f } , { 0.0f, -1.0f } , { 0.0f, 1.0f } , { 1.0f, -1.0f } +}; + +/* + * Gradient directions for 3D. + * These vectors are based on the midpoints of the 12 edges of a cube. + * A larger array of random unit length vectors would also do the job, + * but these 12 (including 4 repeats to make the array length a power + * of two) work better. They are not random, they are carefully chosen + * to represent a small, isotropic set of directions. + */ + +static float grad3lut[16][3] = { + { 1.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 1.0f }, // 12 cube edges + { -1.0f, 0.0f, 1.0f }, { 0.0f, -1.0f, 1.0f }, + { 1.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, -1.0f }, + { -1.0f, 0.0f, -1.0f }, { 0.0f, -1.0f, -1.0f }, + { 1.0f, -1.0f, 0.0f }, { 1.0f, 1.0f, 0.0f }, + { -1.0f, 1.0f, 0.0f }, { -1.0f, -1.0f, 0.0f }, + { 1.0f, 0.0f, 1.0f }, { -1.0f, 0.0f, 1.0f }, // 4 repeats to make 16 + { 0.0f, 1.0f, -1.0f }, { 0.0f, -1.0f, -1.0f } +}; + +static float grad4lut[32][4] = { + { 0.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f, 1.0f, -1.0f }, { 0.0f, 1.0f, -1.0f, 1.0f }, { 0.0f, 1.0f, -1.0f, -1.0f }, // 32 tesseract edges + { 0.0f, -1.0f, 1.0f, 1.0f }, { 0.0f, -1.0f, 1.0f, -1.0f }, { 0.0f, -1.0f, -1.0f, 1.0f }, { 0.0f, -1.0f, -1.0f, -1.0f }, + { 1.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 1.0f, -1.0f }, { 1.0f, 0.0f, -1.0f, 1.0f }, { 1.0f, 0.0f, -1.0f, -1.0f }, + { -1.0f, 0.0f, 1.0f, 1.0f }, { -1.0f, 0.0f, 1.0f, -1.0f }, { -1.0f, 0.0f, -1.0f, 1.0f }, { -1.0f, 0.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 1.0f, 0.0f, -1.0f }, { 1.0f, -1.0f, 0.0f, 1.0f }, { 1.0f, -1.0f, 0.0f, -1.0f }, + { -1.0f, 1.0f, 0.0f, 1.0f }, { -1.0f, 1.0f, 0.0f, -1.0f }, { -1.0f, -1.0f, 0.0f, 1.0f }, { -1.0f, -1.0f, 0.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f, -1.0f, 0.0f }, { 1.0f, -1.0f, 1.0f, 0.0f }, { 1.0f, -1.0f, -1.0f, 0.0f }, + { -1.0f, 1.0f, 1.0f, 0.0f }, { -1.0f, 1.0f, -1.0f, 0.0f }, { -1.0f, -1.0f, 1.0f, 0.0f }, { -1.0f, -1.0f, -1.0f, 0.0f } +}; + +// A lookup table to traverse the simplex around a given point in 4D. +// Details can be found where this table is used, in the 4D noise method. +/* TODO: This should not be required, backport it from Bill's GLSL code! */ +static unsigned char simplex[64][4] = { + {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0}, + {0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0}, + {1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, + {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}}; + +/* --------------------------------------------------------------------- */ + +/* + * Helper functions to compute gradients in 1D to 4D + * and gradients-dot-residualvectors in 2D to 4D. + */ + +float grad1( int hash, float *gx ) { + int h = hash & 15; + *gx = 1.0f + (h & 7); // Gradient value is one of 1.0, 2.0, ..., 8.0 + if (h&8) *gx = - *gx; // Make half of the gradients negative +} + +void grad2( int hash, float *gx, float *gy ) { + int h = hash & 7; + *gx = grad2lut[h][0]; + *gy = grad2lut[h][1]; + return; +} + +void grad3( int hash, float *gx, float *gy, float *gz ) { + int h = hash & 15; + *gx = grad3lut[h][0]; + *gy = grad3lut[h][1]; + *gz = grad3lut[h][2]; + return; +} + +void grad4( int hash, float *gx, float *gy, float *gz, float *gw) { + int h = hash & 31; + *gx = grad4lut[h][0]; + *gy = grad4lut[h][1]; + *gz = grad4lut[h][2]; + *gw = grad4lut[h][3]; + return; +} + +/** 1D simplex noise with derivative. + * If the last argument is not null, the analytic derivative + * is also calculated. + */ +float sdnoise1( float x, int px, float *dnoise_dx) +{ + int i0 = FASTFLOOR(x); + int i1 = i0 + 1; + float x0 = x - i0; + float x1 = x0 - 1.0f; + + float gx0, gx1; + float n0, n1; + float t20, t40, t21, t41; + + float x20 = x0*x0; + float t0 = 1.0f - x20; + // if(t0 < 0.0f) t0 = 0.0f; // Never happens for 1D: x0<=1 always + t20 = t0 * t0; + t40 = t20 * t20; + grad1(perm[i0 % px], &gx0); + n0 = t40 * gx0 * x0; + + float x21 = x1*x1; + float t1 = 1.0f - x21; + // if(t1 < 0.0f) t1 = 0.0f; // Never happens for 1D: |x1|<=1 always + t21 = t1 * t1; + t41 = t21 * t21; + grad1(perm[i1 % px], &gx1); + n1 = t41 * gx1 * x1; + + /* Compute derivative according to: + * *dnoise_dx = -8.0f * t20 * t0 * x0 * (gx0 * x0) + t40 * gx0; + * *dnoise_dx += -8.0f * t21 * t1 * x1 * (gx1 * x1) + t41 * gx1; + */ + *dnoise_dx = t20 * t0 * gx0 * x20; + *dnoise_dx += t21 * t1 * gx1 * x21; + *dnoise_dx *= -8.0f; + *dnoise_dx += t40 * gx0 + t41 * gx1; + *dnoise_dx *= 0.25f; /* Scale derivative to match the noise scaling */ + + // The maximum value of this noise is 8*(3/4)^4 = 2.53125 + // A factor of 0.395 would scale to fit exactly within [-1,1], but + // to better match classic Perlin noise, we scale it down some more. + return 0.25f * (n0 + n1); +} + +float sdnoise1s( float x, float *dnoise_dx) +{ + return sdnoise1(x, 256, dnoise_dx); +} + +/* Skewing factors for 2D simplex grid: + * F2 = 0.5*(sqrt(3.0)-1.0) + * G2 = (3.0-Math.sqrt(3.0))/6.0 + */ +#define F2 0.366025403 +#define G2 0.211324865 + +/** 2D simplex noise with derivatives. + * If the last two arguments are not null, the analytic derivative + * (the 2D gradient of the scalar noise field) is also calculated. + */ +float sdnoise2( float x, float y, int px, int py, float *dnoise_dx, float *dnoise_dy ) +{ + float n0, n1, n2; /* Noise contributions from the three simplex corners */ + float gx0, gy0, gx1, gy1, gx2, gy2; /* Gradients at simplex corners */ + + /* Skew the input space to determine which simplex cell we're in */ + float s = ( x + y ) * F2; /* Hairy factor for 2D */ + float xs = x + s; + float ys = y + s; + int i = FASTFLOOR( xs ); + int j = FASTFLOOR( ys ); + + float t = ( float ) ( i + j ) * G2; + float X0 = i - t; /* Unskew the cell origin back to (x,y) space */ + float Y0 = j - t; + float x0 = x - X0; /* The x,y distances from the cell origin */ + float y0 = y - Y0; + + /* For the 2D case, the simplex shape is an equilateral triangle. + * Determine which simplex we are in. */ + int i1, j1; /* Offsets for second (middle) corner of simplex in (i,j) coords */ + if( x0 > y0 ) { i1 = 1; j1 = 0; } /* lower triangle, XY order: (0,0)->(1,0)->(1,1) */ + else { i1 = 0; j1 = 1; } /* upper triangle, YX order: (0,0)->(0,1)->(1,1) */ + + /* A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + * a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + * c = (3-sqrt(3))/6 */ + float x1 = x0 - i1 + G2; /* Offsets for middle corner in (x,y) unskewed coords */ + float y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2; /* Offsets for last corner in (x,y) unskewed coords */ + float y2 = y0 - 1.0f + 2.0f * G2; + + /* Wrap the integer indices at 256, to avoid indexing perm[] out of bounds */ + int ii = i % px; + int jj = j % py; + + /* Calculate the contribution from the three corners */ + float t0 = 0.5f - x0 * x0 - y0 * y0; + float t20, t40; + if( t0 < 0.0f ) t40 = t20 = t0 = n0 = gx0 = gy0 = 0.0f; /* No influence */ + else { + grad2( perm[ii + perm[jj]], &gx0, &gy0 ); + t20 = t0 * t0; + t40 = t20 * t20; + n0 = t40 * ( gx0 * x0 + gy0 * y0 ); + } + + float t1 = 0.5f - x1 * x1 - y1 * y1; + float t21, t41; + if( t1 < 0.0f ) t21 = t41 = t1 = n1 = gx1 = gy1 = 0.0f; /* No influence */ + else { + grad2( perm[ii + i1 + perm[jj + j1]], &gx1, &gy1 ); + t21 = t1 * t1; + t41 = t21 * t21; + n1 = t41 * ( gx1 * x1 + gy1 * y1 ); + } + + float t2 = 0.5f - x2 * x2 - y2 * y2; + float t22, t42; + if( t2 < 0.0f ) t42 = t22 = t2 = n2 = gx2 = gy2 = 0.0f; /* No influence */ + else { + grad2( perm[ii + 1 + perm[jj + 1]], &gx2, &gy2 ); + t22 = t2 * t2; + t42 = t22 * t22; + n2 = t42 * ( gx2 * x2 + gy2 * y2 ); + } + + /* Add contributions from each corner to get the final noise value. + * The result is scaled to return values in the interval [-1,1]. */ + float noise = 40.0f * ( n0 + n1 + n2 ); + + /* Compute derivative, if requested by supplying non-null pointers + * for the last two arguments */ + if( ( dnoise_dx != 0 ) && ( dnoise_dy != 0 ) ) + { + /* A straight, unoptimised calculation would be like: + * *dnoise_dx = -8.0f * t20 * t0 * x0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gx0; + * *dnoise_dy = -8.0f * t20 * t0 * y0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gy0; + * *dnoise_dx += -8.0f * t21 * t1 * x1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gx1; + * *dnoise_dy += -8.0f * t21 * t1 * y1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gy1; + * *dnoise_dx += -8.0f * t22 * t2 * x2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gx2; + * *dnoise_dy += -8.0f * t22 * t2 * y2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gy2; + */ + float temp0 = t20 * t0 * ( gx0* x0 + gy0 * y0 ); + *dnoise_dx = temp0 * x0; + *dnoise_dy = temp0 * y0; + float temp1 = t21 * t1 * ( gx1 * x1 + gy1 * y1 ); + *dnoise_dx += temp1 * x1; + *dnoise_dy += temp1 * y1; + float temp2 = t22 * t2 * ( gx2* x2 + gy2 * y2 ); + *dnoise_dx += temp2 * x2; + *dnoise_dy += temp2 * y2; + *dnoise_dx *= -8.0f; + *dnoise_dy *= -8.0f; + *dnoise_dx += t40 * gx0 + t41 * gx1 + t42 * gx2; + *dnoise_dy += t40 * gy0 + t41 * gy1 + t42 * gy2; + *dnoise_dx *= 40.0f; /* Scale derivative to match the noise scaling */ + *dnoise_dy *= 40.0f; + } + return noise; +} + +float sdnoise2s( float x, float y, float *dnoise_dx, float *dnoise_dy ) +{ + return sdnoise2( x, y, 256, 256, dnoise_dx, dnoise_dy ); +} + +/* Skewing factors for 3D simplex grid: + * F3 = 1/3 + * G3 = 1/6 */ +#define F3 0.333333333 +#define G3 0.166666667 + + +/** 3D simplex noise with derivatives. + * If the last tthree arguments are not null, the analytic derivative + * (the 3D gradient of the scalar noise field) is also calculated. + */ +float sdnoise3( float x, float y, float z, int px, int py, int pz, + float *dnoise_dx, float *dnoise_dy, float *dnoise_dz ) +{ + float n0, n1, n2, n3; /* Noise contributions from the four simplex corners */ + float noise; /* Return value */ + float gx0, gy0, gz0, gx1, gy1, gz1; /* Gradients at simplex corners */ + float gx2, gy2, gz2, gx3, gy3, gz3; + + /* Skew the input space to determine which simplex cell we're in */ + float s = (x+y+z)*F3; /* Very nice and simple skew factor for 3D */ + float xs = x+s; + float ys = y+s; + float zs = z+s; + int i = FASTFLOOR(xs); + int j = FASTFLOOR(ys); + int k = FASTFLOOR(zs); + + float t = (float)(i+j+k)*G3; + float X0 = i-t; /* Unskew the cell origin back to (x,y,z) space */ + float Y0 = j-t; + float Z0 = k-t; + float x0 = x-X0; /* The x,y,z distances from the cell origin */ + float y0 = y-Y0; + float z0 = z-Z0; + + /* For the 3D case, the simplex shape is a slightly irregular tetrahedron. + * Determine which simplex we are in. */ + int i1, j1, k1; /* Offsets for second corner of simplex in (i,j,k) coords */ + int i2, j2, k2; /* Offsets for third corner of simplex in (i,j,k) coords */ + + /* TODO: This code would benefit from a backport from the GLSL version! */ + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } /* X Y Z order */ + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } /* X Z Y order */ + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } /* Z X Y order */ + } + else { // x0<y0 + if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } /* Z Y X order */ + else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } /* Y Z X order */ + else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } /* Y X Z order */ + } + + /* A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), + * a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and + * a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where + * c = 1/6. */ + + float x1 = x0 - i1 + G3; /* Offsets for second corner in (x,y,z) coords */ + float y1 = y0 - j1 + G3; + float z1 = z0 - k1 + G3; + float x2 = x0 - i2 + 2.0f * G3; /* Offsets for third corner in (x,y,z) coords */ + float y2 = y0 - j2 + 2.0f * G3; + float z2 = z0 - k2 + 2.0f * G3; + float x3 = x0 - 1.0f + 3.0f * G3; /* Offsets for last corner in (x,y,z) coords */ + float y3 = y0 - 1.0f + 3.0f * G3; + float z3 = z0 - 1.0f + 3.0f * G3; + + /* Wrap the integer indices at 256, to avoid indexing perm[] out of bounds */ + int ii = i % px; + int jj = j % py; + int kk = k % pz; + + /* Calculate the contribution from the four corners */ + float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0; + float t20, t40; + if(t0 < 0.0f) n0 = t0 = t20 = t40 = gx0 = gy0 = gz0 = 0.0f; + else { + grad3( perm[ii + perm[jj + perm[kk]]], &gx0, &gy0, &gz0 ); + t20 = t0 * t0; + t40 = t20 * t20; + n0 = t40 * ( gx0 * x0 + gy0 * y0 + gz0 * z0 ); + } + + float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1; + float t21, t41; + if(t1 < 0.0f) n1 = t1 = t21 = t41 = gx1 = gy1 = gz1 = 0.0f; + else { + grad3( perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]], &gx1, &gy1, &gz1 ); + t21 = t1 * t1; + t41 = t21 * t21; + n1 = t41 * ( gx1 * x1 + gy1 * y1 + gz1 * z1 ); + } + + float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2; + float t22, t42; + if(t2 < 0.0f) n2 = t2 = t22 = t42 = gx2 = gy2 = gz2 = 0.0f; + else { + grad3( perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]], &gx2, &gy2, &gz2 ); + t22 = t2 * t2; + t42 = t22 * t22; + n2 = t42 * ( gx2 * x2 + gy2 * y2 + gz2 * z2 ); + } + + float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3; + float t23, t43; + if(t3 < 0.0f) n3 = t3 = t23 = t43 = gx3 = gy3 = gz3 = 0.0f; + else { + grad3( perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]], &gx3, &gy3, &gz3 ); + t23 = t3 * t3; + t43 = t23 * t23; + n3 = t43 * ( gx3 * x3 + gy3 * y3 + gz3 * z3 ); + } + + /* Add contributions from each corner to get the final noise value. + * The result is scaled to return values in the range [-1,1] */ + noise = 28.0f * (n0 + n1 + n2 + n3); + + /* Compute derivative, if requested by supplying non-null pointers + * for the last three arguments */ + if( ( dnoise_dx != 0 ) && ( dnoise_dy != 0 ) && ( dnoise_dz != 0 )) + { + /* A straight, unoptimised calculation would be like: + * *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gx0; + * *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gy0; + * *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gz0; + * *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gx1; + * *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gy1; + * *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gz1; + * *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gx2; + * *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gy2; + * *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gz2; + * *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gx3; + * *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gy3; + * *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gz3; + */ + float temp0 = t20 * t0 * ( gx0 * x0 + gy0 * y0 + gz0 * z0 ); + *dnoise_dx = temp0 * x0; + *dnoise_dy = temp0 * y0; + *dnoise_dz = temp0 * z0; + float temp1 = t21 * t1 * ( gx1 * x1 + gy1 * y1 + gz1 * z1 ); + *dnoise_dx += temp1 * x1; + *dnoise_dy += temp1 * y1; + *dnoise_dz += temp1 * z1; + float temp2 = t22 * t2 * ( gx2 * x2 + gy2 * y2 + gz2 * z2 ); + *dnoise_dx += temp2 * x2; + *dnoise_dy += temp2 * y2; + *dnoise_dz += temp2 * z2; + float temp3 = t23 * t3 * ( gx3 * x3 + gy3 * y3 + gz3 * z3 ); + *dnoise_dx += temp3 * x3; + *dnoise_dy += temp3 * y3; + *dnoise_dz += temp3 * z3; + *dnoise_dx *= -8.0f; + *dnoise_dy *= -8.0f; + *dnoise_dz *= -8.0f; + *dnoise_dx += t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3; + *dnoise_dy += t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3; + *dnoise_dz += t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3; + *dnoise_dx *= 28.0f; /* Scale derivative to match the noise scaling */ + *dnoise_dy *= 28.0f; + *dnoise_dz *= 28.0f; + } + return noise; +} + +float sdnoise3s( float x, float y, float z, float *dnoise_dx, float *dnoise_dy, float *dnoise_dz ) +{ + return sdnoise3(x, y, z, 256, 256, 256, dnoise_dx, dnoise_dy, dnoise_dz); +} + +// The skewing and unskewing factors are hairy again for the 4D case +#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0 +#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0 + +/** 4D simplex noise with derivatives. + * If the last four arguments are not null, the analytic derivative + * (the 4D gradient of the scalar noise field) is also calculated. + */ +float sdnoise4( float x, float y, float z, float w, + int px, int py, int pz, int pw, + float *dnoise_dx, float *dnoise_dy, + float *dnoise_dz, float *dnoise_dw) +{ + float n0, n1, n2, n3, n4; // Noise contributions from the five corners + float noise; // Return value + float gx0, gy0, gz0, gw0, gx1, gy1, gz1, gw1; /* Gradients at simplex corners */ + float gx2, gy2, gz2, gw2, gx3, gy3, gz3, gw3, gx4, gy4, gz4, gw4; + float t20, t21, t22, t23, t24; + float t40, t41, t42, t43, t44; + + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + float s = (x + y + z + w) * F4; // Factor for 4D skewing + float xs = x + s; + float ys = y + s; + float zs = z + s; + float ws = w + s; + int i = FASTFLOOR(xs); + int j = FASTFLOOR(ys); + int k = FASTFLOOR(zs); + int l = FASTFLOOR(ws); + + float t = (i + j + k + l) * G4; // Factor for 4D unskewing + float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space + float Y0 = j - t; + float Z0 = k - t; + float W0 = l - t; + + float x0 = x - X0; // The x,y,z,w distances from the cell origin + float y0 = y - Y0; + float z0 = z - Z0; + float w0 = w - W0; + + // For the 4D case, the simplex is a 4D shape I won't even try to describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a reasonable way of finding the ordering of x,y,z,w + // and then find the correct traversal order for the simplex were in. + // First, six pair-wise comparisons are performed between each possible pair + // of the four coordinates, and then the results are used to add up binary + // bits for an integer index into a precomputed lookup table, simplex[]. + int c1 = (x0 > y0) ? 32 : 0; + int c2 = (x0 > z0) ? 16 : 0; + int c3 = (y0 > z0) ? 8 : 0; + int c4 = (x0 > w0) ? 4 : 0; + int c5 = (y0 > w0) ? 2 : 0; + int c6 = (z0 > w0) ? 1 : 0; + int c = c1 | c2 | c3 | c4 | c5 | c6; // '|' is mostly faster than '+' + + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w + // impossible. Only the 24 indices which have non-zero entries make any sense. + // We use a thresholding to set the coordinates in turn from the largest magnitude. + // The number 3 in the "simplex" array is at the position of the largest coordinate. + i1 = simplex[c][0]>=3 ? 1 : 0; + j1 = simplex[c][1]>=3 ? 1 : 0; + k1 = simplex[c][2]>=3 ? 1 : 0; + l1 = simplex[c][3]>=3 ? 1 : 0; + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0]>=2 ? 1 : 0; + j2 = simplex[c][1]>=2 ? 1 : 0; + k2 = simplex[c][2]>=2 ? 1 : 0; + l2 = simplex[c][3]>=2 ? 1 : 0; + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0]>=1 ? 1 : 0; + j3 = simplex[c][1]>=1 ? 1 : 0; + k3 = simplex[c][2]>=1 ? 1 : 0; + l3 = simplex[c][3]>=1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + float y1 = y0 - j1 + G4; + float z1 = z0 - k1 + G4; + float w1 = w0 - l1 + G4; + float x2 = x0 - i2 + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords + float y2 = y0 - j2 + 2.0f * G4; + float z2 = z0 - k2 + 2.0f * G4; + float w2 = w0 - l2 + 2.0f * G4; + float x3 = x0 - i3 + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords + float y3 = y0 - j3 + 3.0f * G4; + float z3 = z0 - k3 + 3.0f * G4; + float w3 = w0 - l3 + 3.0f * G4; + float x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords + float y4 = y0 - 1.0f + 4.0f * G4; + float z4 = z0 - 1.0f + 4.0f * G4; + float w4 = w0 - 1.0f + 4.0f * G4; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + int ii = i % px; + int jj = j % py; + int kk = k % pz; + int ll = l % pw; + + // Calculate the contribution from the five corners + float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0 - w0*w0; + if(t0 < 0.0f) n0 = t0 = t20 = t40 = gx0 = gy0 = gz0 = gw0 = 0.0f; + else { + t20 = t0 * t0; + t40 = t20 * t20; + grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], &gx0, &gy0, &gz0, &gw0); + n0 = t40 * ( gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0 ); + } + + float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1 - w1*w1; + if(t1 < 0.0f) n1 = t1 = t21 = t41 = gx1 = gy1 = gz1 = gw1 = 0.0f; + else { + t21 = t1 * t1; + t41 = t21 * t21; + grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], &gx1, &gy1, &gz1, &gw1); + n1 = t41 * ( gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1 ); + } + + float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2 - w2*w2; + if(t2 < 0.0f) n2 = t2 = t22 = t42 = gx2 = gy2 = gz2 = gw2 = 0.0f; + else { + t22 = t2 * t2; + t42 = t22 * t22; + grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], &gx2, &gy2, &gz2, &gw2); + n2 = t42 * ( gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2 ); + } + + float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3 - w3*w3; + if(t3 < 0.0f) n3 = t3 = t23 = t43 = gx3 = gy3 = gz3 = gw3 = 0.0f; + else { + t23 = t3 * t3; + t43 = t23 * t23; + grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], &gx3, &gy3, &gz3, &gw3); + n3 = t43 * ( gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3 ); + } + + float t4 = 0.6f - x4*x4 - y4*y4 - z4*z4 - w4*w4; + if(t4 < 0.0f) n4 = t4 = t24 = t44 = gx4 = gy4 = gz4 = gw4 = 0.0f; + else { + t24 = t4 * t4; + t44 = t24 * t24; + grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], &gx4, &gy4, &gz4, &gw4); + n4 = t44 * ( gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4 ); + } + + // Sum up and scale the result to cover the range [-1,1] + noise = 27.0f * (n0 + n1 + n2 + n3 + n4); // TODO: The scale factor is preliminary! + + /* Compute derivative, if requested by supplying non-null pointers + * for the last four arguments */ + if( ( dnoise_dx != 0 ) && ( dnoise_dy != 0 ) && ( dnoise_dz != 0 ) && ( dnoise_dw != 0 ) ) + { + /* A straight, unoptimised calculation would be like: + * *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gx0; + * *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gy0; + * *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gz0; + * *dnoise_dw = -8.0f * t20 * t0 * w0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gw0; + * *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gx1; + * *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gy1; + * *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gz1; + * *dnoise_dw = -8.0f * t21 * t1 * w1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gw1; + * *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gx2; + * *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gy2; + * *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gz2; + * *dnoise_dw += -8.0f * t22 * t2 * w2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gw2; + * *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gx3; + * *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gy3; + * *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gz3; + * *dnoise_dw += -8.0f * t23 * t3 * w3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gw3; + * *dnoise_dx += -8.0f * t24 * t4 * x4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gx4; + * *dnoise_dy += -8.0f * t24 * t4 * y4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gy4; + * *dnoise_dz += -8.0f * t24 * t4 * z4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gz4; + * *dnoise_dw += -8.0f * t24 * t4 * w4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gw4; + */ + float temp0 = t20 * t0 * ( gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0 ); + *dnoise_dx = temp0 * x0; + *dnoise_dy = temp0 * y0; + *dnoise_dz = temp0 * z0; + *dnoise_dw = temp0 * w0; + float temp1 = t21 * t1 * ( gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1 ); + *dnoise_dx += temp1 * x1; + *dnoise_dy += temp1 * y1; + *dnoise_dz += temp1 * z1; + *dnoise_dw += temp1 * w1; + float temp2 = t22 * t2 * ( gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2 ); + *dnoise_dx += temp2 * x2; + *dnoise_dy += temp2 * y2; + *dnoise_dz += temp2 * z2; + *dnoise_dw += temp2 * w2; + float temp3 = t23 * t3 * ( gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3 ); + *dnoise_dx += temp3 * x3; + *dnoise_dy += temp3 * y3; + *dnoise_dz += temp3 * z3; + *dnoise_dw += temp3 * w3; + float temp4 = t24 * t4 * ( gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4 ); + *dnoise_dx += temp4 * x4; + *dnoise_dy += temp4 * y4; + *dnoise_dz += temp4 * z4; + *dnoise_dw += temp4 * w4; + *dnoise_dx *= -8.0f; + *dnoise_dy *= -8.0f; + *dnoise_dz *= -8.0f; + *dnoise_dw *= -8.0f; + *dnoise_dx += t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3 + t44 * gx4; + *dnoise_dy += t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3 + t44 * gy4; + *dnoise_dz += t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3 + t44 * gz4; + *dnoise_dw += t40 * gw0 + t41 * gw1 + t42 * gw2 + t43 * gw3 + t44 * gw4; + + *dnoise_dx *= 28.0f; /* Scale derivative to match the noise scaling */ + *dnoise_dy *= 28.0f; + *dnoise_dz *= 28.0f; + *dnoise_dw *= 28.0f; + } + + return noise; +} + +float sdnoise4s( float x, float y, float z, float w, + float *dnoise_dx, float *dnoise_dy, + float *dnoise_dz, float *dnoise_dw) +{ + return sdnoise4( x, y, z, w, + 256, 256, 256, 256, + dnoise_dx, dnoise_dy, + dnoise_dz, dnoise_dw); +} diff --git a/src/sdnoise1234.h b/src/sdnoise1234.h new file mode 100644 index 0000000000..0ddd885880 --- /dev/null +++ b/src/sdnoise1234.h @@ -0,0 +1,84 @@ +/* sdnoise1234, Simplex noise with true analytic + * derivative in 1D to 4D. + * + * Copyright © 2003-2008, Stefan Gustavson + * + * Contact: stefan.gustavson@gmail.com + * + * This library 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 2 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \brief C header file for Perlin simplex noise with analytic + derivative over 1, 2, 3 and 4 dimensions. + \author Stefan Gustavson (stefan.gustavson@gmail.com) + \author Charl van Deventer (landon.skyfire@gmail.com) + */ + +/* + * This is an implementation of Perlin "simplex noise" over one + * dimension (x), two dimensions (x,y), three dimensions (x,y,z) + * and four dimensions (x,y,z,w). The analytic derivative is + * returned, to make it possible to do lots of fun stuff like + * flow animations, curl noise, analytic antialiasing and such. + * + * Visually, this noise is exactly the same as the plain version of + * simplex noise provided in the file "snoise1234.c". It just returns + * all partial derivatives in addition to the scalar noise value. + * + */ + +/* + * 23 June 2010: Modified by Charl van Deventer to allow periodic arguments + * Note: It doesn't check for bounds over 255 (wont work) and might fail with + * negative coords. + */ + +#include <math.h> + +/** 1D simplex noise with derivative. + * If the last argument is not null, the analytic derivative + * is also calculated. + */ +float sdnoise1s( float x, float *dnoise_dx); +float sdnoise1( float x, int px, float *dnoise_dx); + +/** 2D simplex noise with derivatives. + * If the last two arguments are not null, the analytic derivative + * (the 2D gradient of the scalar noise field) is also calculated. + */ +float sdnoise2s( float x, float y, float *dnoise_dx, float *dnoise_dy ); +float sdnoise2( float x, float y, int px, int py, float *dnoise_dx, float *dnoise_dy ); + +/** 3D simplex noise with derivatives. + * If the last tthree arguments are not null, the analytic derivative + * (the 3D gradient of the scalar noise field) is also calculated. + */ +float sdnoise3s( float x, float y, float z, + float *dnoise_dx, float *dnoise_dy, float *dnoise_dz ); +float sdnoise3( float x, float y, float z, int px, int py, int pz, + float *dnoise_dx, float *dnoise_dy, float *dnoise_dz ); + +/** 4D simplex noise with derivatives. + * If the last four arguments are not null, the analytic derivative + * (the 4D gradient of the scalar noise field) is also calculated. + */ +float sdnoise4s( float x, float y, float z, float w, + float *dnoise_dx, float *dnoise_dy, + float *dnoise_dz, float *dnoise_dw); +float sdnoise4( float x, float y, float z, float w, + int px, int py, int pz, int pw, + float *dnoise_dx, float *dnoise_dy, + float *dnoise_dz, float *dnoise_dw); -- GitLab