Clock

All new ideas for the upcoming releases of ToME 4.x.x should be discussed here

Moderator: Moderator

Post Reply
Message
Author
lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Clock

#1 Post by lukep »

Add a perfectly precise (in-game) clock to the game, that tells you the time to the next cooldown/timed effect tick/etc in both a portion of a normal turn, and of each of your actions. Example:

12:34:56.07,
0.93 turns to next CD/timed effect count. In that time you can do:

7.12 steps
1.13 other actions
1.13 attacks
1.13 spell casts
1.13 mind casts
1.13 summons

(include only up to "other actions" if no other modifiers are present)


PS: know, non-trivial issue: if you get hit with a global speed modifier in the middle of your recovery, the numbers will be wrong. IIRC, cripple, stun, etc... shouldn't matter.

EDIT: started coding this...

Code: Select all

use_power = { name = "check the time", power = 1,
		use = function(self, who)
			local turn = (1000 * game.player.global_speed * (0.9 - (game.turn/10) + math.floor(game.turn/10)) + game.player.energy.value - 1000)
			game.logPlayer(who, "It is turn %0.1f, and you have %d stored energy", game.turn/10, game.player.energy.value - 1000)
			game.logPlayer(who, "%d energy available before the next turn", turn)
			return false
		end
	},
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

jotwebe
Uruivellas
Posts: 725
Joined: Fri Apr 15, 2011 6:58 am
Location: GMT+1

Re: Clock

#2 Post by jotwebe »

Neat!

I wonder about displaying things the other way around, i.e. :

Code: Select all

Actions will take the following amounts of a turn:

step:     0.14 
other:    0.88
attack:   0.88
...
And then display it graphically as fractions of a circle - maybe displaying your values on mouseover.

With the circle, you could see at a glace that you could take lots of steps, but won't be able to take other actions twice.

The different types of actions could be arranged concentrically, with global speed getting the outer circle and other types having smaller diameters as needed.
Ghoul never existed, this never happened!

nate
Wyrmic
Posts: 261
Joined: Fri Jan 18, 2013 8:35 am

Re: Clock

#3 Post by nate »

Arcs/wedges would be a nice interface-- maybe noon could represent the next global turn, so you could see at a glance how your actions would affect your cooldowns and interact with default speed critters.

But what would you do if you happened to be moving slower than normal? Spirals instead of arcs? Maybe just a big ugly skull in place of your clock :)
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on

jotwebe
Uruivellas
Posts: 725
Joined: Fri Apr 15, 2011 6:58 am
Location: GMT+1

Re: Clock

#4 Post by jotwebe »

nate wrote:Arcs/wedges would be a nice interface-- maybe noon could represent the next global turn, so you could see at a glance how your actions would affect your cooldowns and interact with default speed critters.
Exactly what I was shooting for.
nate wrote: But what would you do if you happened to be moving slower than normal? Spirals instead of arcs? Maybe just a big ugly skull in place of your clock :)
Yeah, that's the problem. One way would be to use a different colour, and you can see the wedge shrink counterclockwise as your energy surplus gets used up. But with all the different speeds we may want to be using different colours anway. Especially confusing if your global speed is slower than 100%, but your move speed quicker, or something like that.
Ghoul never existed, this never happened!

lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Re: Clock

#5 Post by lukep »

That UI idea looks like it should work, and if someone wants to do it, I'd cheer them on.

My current goal for the clock's output looks like this:

It is turn 1215.4, and you have 35 stored energy
535 energy available before the next turn
900 energy to move
500 energy to attack
1000 energy for most other actions

Thoughts?
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Re: Clock

#6 Post by lukep »

95% complete. I now have move/attack/spell/mind/summon speeds, and they are all hidden when not in use. I think the only things left are the fungus effects, and making it so that attack speed works for weapons with non-100% attack speed (which looks quite difficult).

Possible additions are: "if you (attack/move/cast), your next turn will be in 0.9 turns" or something like that added to each line.

Code: Select all

	use_power = { name = "check the time", power = 1,
		use = function(self, who)
			local me = game.player
			local turn = (1000 * me.global_speed * (0.9 - (game.turn/10) + math.floor(game.turn/10)) + me.energy.value - 1000)
			local move = me:combatMovementSpeed() * 1000
			local hit = me:combatSpeed() * 1000
			local spell = me:combatSpellSpeed() * 1000
			local mind = me:combatMindSpeed() * 1000
			local summon = me:combatSummonSpeed() * 1000

			game.logPlayer(who, "It is turn %0.1f, and you have %d stored energy", game.turn/10, me.energy.value - 1000)
			game.logPlayer(who, "%d energy available before the next turn", turn)
			if move ~= 1000 then game.logPlayer(who, "%d energy to move.", move) end
			if hit ~= 1000 then game.logPlayer(who, "%d energy to attack.", hit) end
			if spell ~= 1000 then game.logPlayer(who, "%d energy to cast a spell.", spell) end
			if mind ~= 1000 then game.logPlayer(who, "%d energy to use a mind power.", mind) end
			if summon ~= 1000 then game.logPlayer(who, "%d energy to summon.", summon) end
			game.logPlayer(who, "%d energy for normal actions.", 1000)

			return false
		end
	},
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

jenx
Sher'Tul Godslayer
Posts: 2263
Joined: Mon Feb 14, 2011 11:16 pm

Re: Clock

#7 Post by jenx »

wow - when would this be available as an addon?
MADNESS rocks

lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Re: Clock

#8 Post by lukep »

Based on my last few attempts, at least a couple of days to get something working properly.

In the mean time, here's the addon I was testing and developing it in. It modifies the orb of scrying, so it is only available to certain races, and also makes everything auto ID (like the SVN change).

To use it, just activate the orb, it will print out the message without taking any time/power.
Attachments
tome-test.zip
(23.85 KiB) Downloaded 197 times
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

nate
Wyrmic
Posts: 261
Joined: Fri Jan 18, 2013 8:35 am

Re: Clock

#9 Post by nate »

Working prototype code for a graphical display similar to that described by jotwebe.

Works with minimalist UI (only). Just a prototype at this point, but it works, and comments are appreciated.
Noon faces east, rather than north.
Default speeds (other than global) aren't shown. Speeds are shown in the colors of a resource associated with that speed.
Because of the way the game works (acting only at integer value ticks), a constant, average rate isn't the same thing as "when will i move next?" I've made it to be precise about when you will get to act next. Because of that, even at a constant speed, you may see the lengths of the arcs change slightly, as your free energy fluctuates.
If you have enough energy to act instantly, a dot is shown rather than an arc. If you're running slower than default, you'll see the extent of your slowness by a line tangent to the arc, to show how much longer than a turn you'll need to recover from this action.
Code is pretty well annotated, but ugly; it's got too many remnants of failed attempts left in there.

To place in mod/class/uiset/minimalist.lua/_M:displayResources, between Ammo and Hourglass sections (although you could put it just about anyplace in there):

Code: Select all

		-----------------------------------------------------------------------------------

		game.player.displayClock = true
		--control this from elsewhere
		
		
		--a lot of these functions don't really belong in this file, but I'm concerned about scope
		local drawLine = function (xStart, yStart, xEnd, yEnd, dotDensity, color, shader)
		--draws a line of a certain dot density
			local x, y = xStart, yStart
			local length = math.sqrt((x-xEnd)^2+(y-yEnd)^2)
			local step = 0
			local xSlope, ySlope = (xEnd - xStart)/length, (yEnd -yStart)/length
			while step < length do
				shat[1]:toScreenPrecise(x, y, dotDensity, dotDensity, 0, 1, 0, 1, color[1], color[2], color[3], 1)
				step = step + 1/dotDensity
				x = x+xSlope
				y = y+ySlope
			end
		end

		local fixAngles = function (angle)
			while angle >= 2*math.pi do
				angle = angle - 2*math.pi
			end
			while angle < 0 do
				angle = angle + 2*math.pi
			end
			return angle
		end		
		
		local polarToCartesian = function (angle, radius)
			return radius*math.cos(fixAngles(angle)), radius*math.sin(fixAngles(angle))
		end
		
		local drawArc = function (xCenter, yCenter, radius, angleStart, angleEnd, numCorners, dotDensity, color, shader)
		--draws an arc centered on, of radius, starting at, ending at, of certain density and color; angle 0 is East, angle pi/2 is South
		--if you send it identical numbers for angleStart and angleEnd, it will draw a circle
			if numCorners < 3 then return nil end
			local start, stop = fixAngles(angleStart), fixAngles(angleEnd)
			if stop <= start then stop = stop + 2*math.pi end
			local corners = math.ceil(numCorners*(stop-start)/(2*math.pi))
			--corners is the number of actual lines our arc will be composed of, whereas numCorners is the number of corners if the arc were to describe an entire circle
			for count = 1, corners do
				local angleNext = start+(2*math.pi/numCorners)
				if angleNext > stop then angleNext = stop end
				local xStart, yStart = polarToCartesian(start, radius)
				local xStop, yStop = polarToCartesian(angleNext, radius)
				drawLine(xStart+xCenter, yStart+yCenter, xStop+xCenter, yStop+yCenter, dotDensity, color, shader)
				start = angleNext
			end
			return true
		end	
		


		if game.player.displayClock then
		
			--constants
			local radiusMax = 21 
			local radiusMin = 11 
			local dotDensity = 1 --how many dots we draw per pixel
			local cornerDensity = 24 --not scaling to circumference anymore
			
			local xCenter = x + radiusMax
			local yCenter = y + radiusMax
			
			local ticksUntilNextAction = function (speed, globalSpeed, energyValue)
			--returns number of ticks until creature with given speed, global speed, and initial energy value can act again following an action at speed
			--also returns energy value at the next tick the creature can act
				local thresholdToAct = 1000
				local energyPerTick = 100
				energyValue = energyValue - speed*1000
				return math.ceil((thresholdToAct-energyValue)/(energyPerTick*globalSpeed)), energyValue + 100*globalSpeed*math.ceil((thresholdToAct-energyValue)/(energyPerTick*globalSpeed))
			end	
			
			
			
			local createFraction = function (fraction, globalSpeed, energyValue, angleStart, xCenter, yCenter, radiusCurrent, radiusMax, cornerDensity, dotDensity, color, shader)
				if fraction then
					local ticksUntilNext, energyAtNext = ticksUntilNextAction (fraction, globalSpeed, energyValue)
					local angleEnd = angleStart + ticksUntilNext*2*math.pi/10
					if ticksUntilNext >= 10 then
						angleEnd = angleStart
					end
					if ticksUntilNext > 0 then
						drawArc(xCenter, yCenter, radiusCurrent, angleStart, angleEnd, cornerDensity, dotDensity, color, shader)
					end
					if ticksUntilNext > 10 and energyValue >= 1000 then
						local cosine, sine, length = math.cos(angleStart), math.sin(angleStart), 2*radiusMax*(ticksUntilNext-10)/10
						--scaling this appropriately just looks wrong, so I'm scaling it down by pi-- appropriate formula should be 2*math.pi*radiusMax*(ticksUntilNext-10)/10
						drawLine(xCenter+radiusCurrent*cosine, yCenter+radiusCurrent*sine, xCenter+(radiusCurrent*cosine)+length*sine, yCenter+(radiusCurrent*sine)+length*cosine, dotDensity, color, shader)
						--this is executing between player actions with unforseen effects-- is there some way to only call this if waiting on input?
						--my solution: don't call unless energy value over the threshold, seems to work, we'll see if it bugs out when i get to mouse-over mobs
					end
					if ticksUntilNext <= 0 then
						local x, y = polarToCartesian(angleStart, radiusCurrent)
						dotDensity = dotDensity * 2
						shat[1]:toScreenPrecise(xCenter+x - (0.5*dotDensity), yCenter+y - (0.5*dotDensity), dotDensity, dotDensity, 0, 1, 0, 1, color[1], color[2], color[3], 1)
					end
					return true
				else
					return false
				end
			end
			
			--run this for each displayed clock, ugly code
			local energyTable = {globalFraction = nil, combatFraction = nil, movementFraction = nil, spellFraction = nil, mindFraction = nil, summonFraction = nil, energyRemaining = nil, numElements = 0}
			energyTable.globalFraction = game.player.global_speed or 1
			energyTable.numElements = energyTable.numElements + 1
			if game.player:combatSpeed() ~= 1 then
				energyTable.combatFraction = game.player:combatSpeed()
				energyTable.numElements = energyTable.numElements + 1
			end
			if game.player:combatMovementSpeed() ~= 1 then
				energyTable.movementFraction = game.player:combatMovementSpeed()
				energyTable.numElements = energyTable.numElements + 1
			end
			if game.player:combatSpellSpeed() ~= 1 then
				energyTable.spellFraction = game.player:combatSpellSpeed()
				energyTable.numElements = energyTable.numElements + 1
			end
			if game.player:combatMindSpeed() ~= 1 then
				energyTable.mindFraction = game.player:combatMindSpeed()
				energyTable.numElements = energyTable.numElements + 1
			end
			if game.player:combatSummonSpeed() ~= 1 then
				energyTable.summonFraction = game.player.combatSummonSpeed()
				energyTable.numElements = energyTable.numElements + 1
			end
			energyTable.energyRemaining = game.player.energy.value or 0
			
			--I have no idea why # isn't working, but that's why we need numElements, pain in the ass
			
			
			local angleStart = ((game.turn%10)/10)*2*math.pi
			local radiusPerArc, radiusCurrent = nil, nil
			if energyTable.numElements <= 1 then
				radiusCurrent = radiusMax
				radiusPerArc = 0
			end
			if energyTable.numElements > 1 then
				radiusCurrent = radiusMin
				radiusPerArc = (radiusMax-radiusMin)/(energyTable.numElements-1)
			end
			--draw shadow, underlay?
			if createFraction (energyTable.summonFraction, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusCurrent, radiusMax, cornerDensity, dotDensity, equi_c) then
				radiusCurrent = radiusCurrent + radiusPerArc end
			if createFraction (energyTable.mindFraction, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusCurrent, radiusMax, cornerDensity, dotDensity, psi_c) then
				radiusCurrent = radiusCurrent + radiusPerArc end
			if createFraction (energyTable.spellFraction, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusCurrent, radiusMax, cornerDensity, dotDensity, mana_c) then
				radiusCurrent = radiusCurrent + radiusPerArc end
			if createFraction (energyTable.combatFraction, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusCurrent, radiusMax, cornerDensity, dotDensity, stam_c) then
				radiusCurrent = radiusCurrent + radiusPerArc end
			if createFraction (energyTable.movementFraction, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusCurrent, radiusMax,  cornerDensity, dotDensity, air_c) then
				radiusCurrent = radiusCurrent + radiusPerArc end
			createFraction (1, energyTable.globalFraction, energyTable.energyRemaining, angleStart, xCenter, yCenter, radiusMax, radiusMax, cornerDensity, dotDensity, life_c)
			--note that by this time, radiusCurrent should equal radiusMax anyways
			
			drawLine(xCenter, yCenter, xCenter + radiusMax + 1, yCenter, 1, {0,0,0}, shader)
			--draw our noon marker, which is actually at 3 o'clock, so that when we get to mob mouseover, we can just mirror the mob's clock for easy comparison
		
			--todo:
			--if we're mousing over a mob, offset our x, and display their clock to the right of ours
			--if we're mousing over our clock, display tooltip giving numerical values
			--correct our x and y for the next ui element			
			--pretty pictures?
		end
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on

jenx
Sher'Tul Godslayer
Posts: 2263
Joined: Mon Feb 14, 2011 11:16 pm

Re: Clock

#10 Post by jenx »

Can this be put in an addon, for the standard 1.0 Tome?
MADNESS rocks

jenx
Sher'Tul Godslayer
Posts: 2263
Joined: Mon Feb 14, 2011 11:16 pm

Re: Clock

#11 Post by jenx »

lukep wrote:95% complete. I now have move/attack/spell/mind/summon speeds, and they are all hidden when not in use. I think the only things left are the fungus effects, and making it so that attack speed works for weapons with non-100% attack speed (which looks quite difficult).

Possible additions are: "if you (attack/move/cast), your next turn will be in 0.9 turns" or something like that added to each line.

Code: Select all

	use_power = { name = "check the time", power = 1,
		use = function(self, who)
			local me = game.player
			local turn = (1000 * me.global_speed * (0.9 - (game.turn/10) + math.floor(game.turn/10)) + me.energy.value - 1000)
			local move = me:combatMovementSpeed() * 1000
			local hit = me:combatSpeed() * 1000
			local spell = me:combatSpellSpeed() * 1000
			local mind = me:combatMindSpeed() * 1000
			local summon = me:combatSummonSpeed() * 1000

			game.logPlayer(who, "It is turn %0.1f, and you have %d stored energy", game.turn/10, me.energy.value - 1000)
			game.logPlayer(who, "%d energy available before the next turn", turn)
			if move ~= 1000 then game.logPlayer(who, "%d energy to move.", move) end
			if hit ~= 1000 then game.logPlayer(who, "%d energy to attack.", hit) end
			if spell ~= 1000 then game.logPlayer(who, "%d energy to cast a spell.", spell) end
			if mind ~= 1000 then game.logPlayer(who, "%d energy to use a mind power.", mind) end
			if summon ~= 1000 then game.logPlayer(who, "%d energy to summon.", summon) end
			game.logPlayer(who, "%d energy for normal actions.", 1000)

			return false
		end
	},
ok - I've been trying out this addon. What I don't get is that my archer has 80% attack speed, but I never see 800 energy to attack, meaning that hit = 1000 all the time. But the energy available for the next turn changes as you adjust rapid shot, so clearly the energy per attack IS changing.

The value 800 I think is stored in game.energy_to_act. Isn't this the key figure we need to determine what we can do in the current turn?

I'm confused I think! :?
Last edited by jenx on Mon Feb 18, 2013 2:23 am, edited 1 time in total.
MADNESS rocks

lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Re: Clock

#12 Post by lukep »

jenx wrote: been trying out this addon. What I don't get is that my archer has 80% attack speed, but I never see 800 energy to attack, meaning that hit = 1000 all the time. But the energy available for the next turn changes as you adjust rapid shot, so clearly the energy per attack IS changing.
lukep wrote:I think the only things left are the fungus effects, and making it so that attack speed works for weapons with non-100% attack speed (which looks quite difficult).
But I have an idea now...

The reason that Rapid Shot affects it is that it is altering self.combat_speed, not the weapon's combat speed, like quick weapons or bows. The main gameplay difference between the two is that self.combat_speed affects all techniques, and weapon combat speed only affects attacks, if I'm reading code right.
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

lukep
Sher'Tul Godslayer
Posts: 1712
Joined: Mon Mar 14, 2011 10:32 am
Location: Canada

Re: Clock

#13 Post by lukep »

Done, mostly thanks to Zizzo's online indicator addon. Addon forum link. Download Link.
Some of my tools for helping make talents:
Melee Talent Creator
Annotated Talent Code (incomplete)

Post Reply