Related
This question already has answers here:
Is it possible to implement gradual movement of an object to given coordinates in Pygame?
(1 answer)
How to make a circle move diagonally from corner to corner in pygame
(1 answer)
How to move the object in squared path repeatedly?
(1 answer)
Closed 5 months ago.
Currently i'm making Space invaders, and I'm having trouble making the aliens move properly from to the side and down the screen. The aliens are supposed to move one direction, then once they hit the edge of the screen they're meant to go down the screen a little bit, then change direction, and repeat this until they get to the bottom of the screen where the player is. So far this is what I have as a part of my code...
for element in smallInvaders:
element.move_ip(dx,dy)
for element in smallInvaders:
if element.bottom > 100:
dy = 0
dx = 1
if element.right >= width or element.left <= 0:
dy += 0.5
dx = 0
invaderDirSwapped = True
for element in medInvaders:
element.move_ip(dx,dy)
if not invaderDirSwapped:
for element in medInvaders:
if element.right >= width or element.left <= 0:
dy += 0.5
dx = 0
invaderDirSwapped = True
for element in bigInvaders:
element.move_ip(dx,dy)
if not invaderDirSwapped:
for element in bigInvaders:
if element.right >= width or element.left <= 0 :
dy += 0.5
dx = 0
invaderDirSwapped = True
Essentially, smallInvaders, medInvaders and bigInvaders are all lists, where each object in the list is a rect object, that act as the aliens. I tried to make it so that I change the direction y value (dy) when the aliens touch the side of the screen (they're originally moving to the left as dx is -1, and dy is 0 initially), then the dy value gets put to 0, when the y value of the aliens hits a specific amount, but this doesn't seem to work, as the next time through when the aliens go from right to left, they either get stuck, or phase off the side of the screen. I understand both if statements are being executed at the same time, but i'm stuck on what to do because of it, and if there are any other solutions towards fixing the problem.
here's my full code...
import pygame
import sys
import time
import random
#initialize pygame
pygame.mixer.init()
pygame.init()
width = 800
height = 600
# set the size for the surface (screen)
screen = pygame.display.set_mode((width, height),pygame.FULLSCREEN)
width = screen.get_width()
height = screen.get_height()
print(width)
print(height)
# set the caption for the screen
pygame.display.set_caption("Space Invaders")
score = 0
def checkCollision(missles, type, score, invader_kill):
for missle in missles:
collision = missle.collidelist((type))
if collision > -1:
type.pop(collision)
invader_kill.play()
missles.remove(missle)
missle.move_ip(0,missleSpeed)
pygame.draw.rect(screen, WHITE, missle,0)
# define colours you will be using
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
YELLOW = (255,255,0)
shoot_sound = pygame.mixer.Sound("Music-sfx/shoot.wav")
invader_kill = pygame.mixer.Sound("Music-sfx/invaderkilled.wav")
ship_kill = pygame.mixer.Sound("Music-sfx/explosion.wav")
clock = pygame.time.Clock()
FPS = 60
s = 25
#load and scale images
smallInvaderImg = pygame.image.load("images/smallinvader.png")
smallInvaderImg = pygame.transform.scale(smallInvaderImg,(s,s))
medInvaderImg = pygame.image.load("images/crabinvader.png")
medInvaderImg = pygame.transform.scale(medInvaderImg, (s,s))
bigInvaderImg = pygame.image.load("images/biginvader.png")
bigInvaderImg = pygame.transform.scale(bigInvaderImg, (s,s))
shipImg = pygame.image.load("images/ship.png")
shipImg = pygame.transform.scale(shipImg, (60,60))
shieldImg = pygame.image.load("images/shield.png")
shieldImg = pygame.transform.scale(shieldImg , (80,55))
smallInvaders = []
medInvaders = []
bigInvaders = []
enemiesMap = ["sssssssssss",
"mmmmmmmmmmm",
"mmmmmmmmmmm",
"bbbbbbbbbbb"]
invadertype = [smallInvaders,medInvaders,bigInvaders]
dx = -1
dy = 0
x = 240
y = 45
gap = 10
for element in enemiesMap:
for char in element:
if char == "s":
smallInvaders.append(pygame.Rect(x,y,s,s))
elif char == "m":
medInvaders.append(pygame.Rect(x,y,s,s))
elif char == "b":
bigInvaders.append(pygame.Rect(x,y,s,s))
x += s + gap
y += s + gap
x = 240
ship = pygame.Rect(width/2,525,60,60)
shield1 = pygame.Rect(40,370,80,60)
shield2 = pygame.Rect(250,370,80,60)
shield3 = pygame.Rect(460,370,80,60)
shield4 = pygame.Rect(670,370,80,60)
if ship.right == width:
ship.right = width
#missles
maxMissles = 3
missleSpeed = -6
missleWidth = 5
missleHeight = 30
enemmissleWidth = 5
enemmissleHeight = 25
missles = []
missleFired = False
lives = 3
playbutton = pygame.Rect(width/2,height/2,200,90)
playbutton.center = (width/2,height/2)
quitbutton = pygame.Rect(width/2,height/2,200,90)
quitbutton.center = (width/2,height/2+110)
playagn = pygame.Rect(width/2,height/2,235,90)
playagn.center = (width/2,height/2)
playword = pygame.font.Font("pixeltext.ttf", 35)
title = pygame.font.Font("pixeltext.ttf", 80)
quitword = pygame.font.Font("pixeltext.ttf",35)
endscreen = pygame.font.Font("pixeltext.ttf", 90)
playagaintext = pygame.font.Font("pixeltext.ttf", 35)
gameover = pygame.font.Font("pixeltext.ttf",120 )
livestext = pygame.font.Font("pixeltext.ttf",25)
enemMissleFire = False
enemmislist = []
enemymissle = (pygame.Rect(ship.centerx,y,enemmissleWidth,enemmissleHeight))
Cont = False
invaderDirSwapped = False
screenControl = 0
main = True
while main:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
for element in smallInvaders:
element.move_ip(dx,dy)
for element in smallInvaders:
if element.bottom > 100:
dy = 0
dx = 1
if element.right >= width or element.left <= 0:
dy += 0.5
dx = 0
invaderDirSwapped = True
for element in medInvaders:
element.move_ip(dx,dy)
if not invaderDirSwapped:
for element in medInvaders:
if element.right >= width or element.left <= 0:
dy += 0.5
dx = 0
invaderDirSwapped = True
for element in bigInvaders:
element.move_ip(dx,dy)
if not invaderDirSwapped:
for element in bigInvaders:
if element.right >= width or element.left <= 0 :
dy += 0.5
dx = 0
invaderDirSwapped = True
key_input = pygame.key.get_pressed()
if key_input[pygame.K_RIGHT] and ship.right < width:
ship.move_ip(4,0)
if key_input[pygame.K_LEFT] and ship.left > 0:
ship.move_ip(-4,0)
if key_input[pygame.K_SPACE] and not missleFired:
missleFired = True
enemmissleFire = True
shoot_sound.play()
missles.append(pygame.Rect(ship.centerx,ship.top,missleWidth,missleHeight))
if screenControl == 0:
screen.fill(BLACK)
texttitle = title.render("SPACE INVADERS", True, WHITE)
textrect = texttitle.get_rect()
textrect.center = (width/2, 100)
screen.blit(texttitle,textrect)
if playbutton.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
screenControl = 1
lives = 3
pygame.draw.rect(screen,WHITE,(playbutton), 0)
if playbutton.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE, (playbutton), 4)
if quitbutton.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
main = False
pygame.draw.rect(screen,WHITE,(quitbutton), 0)
if quitbutton.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE,quitbutton,4)
textplay = playword.render("PLAY", True, BLUE)
textrect2 = textplay.get_rect()
textrect2.center = (width/2,height/2)
screen.blit(textplay,textrect2)
textquit = quitword.render("QUIT",True,BLUE)
textrect3 = textquit.get_rect()
textrect3.center = (width/2,height/2+110)
screen.blit(textquit,textrect3)
if screenControl == 1:
screen.fill(BLACK)
if len(missles) > 0:
if missleFired and missles[-1].bottom < (ship.top - 120) and not key_input[pygame.K_SPACE]:
missleFired = False
if len(missles) == 0:
missleFired = False
for invader in smallInvaders:
screen.blit(smallInvaderImg, invader)
for invader in medInvaders:
screen.blit(medInvaderImg, invader)
for invader in bigInvaders:
screen.blit(bigInvaderImg, invader)
screen.blit(shipImg,ship)
screen.blit(shieldImg,shield1)
screen.blit(shieldImg,shield2)
screen.blit(shieldImg,shield3)
screen.blit(shieldImg,shield4)
allinvaders = [smallInvaders,medInvaders,bigInvaders]
randinvader = random.choice(allinvaders)
if randinvader == smallInvaders:
if len(smallInvaders) >= 1:
abc = random.choice(smallInvaders)
elif randinvader == medInvaders:
if len(medInvaders) >= 1:
abc = random.choice(medInvaders)
elif randinvader == bigInvaders:
if len(bigInvaders) >= 1:
abc = random.choice(bigInvaders)
move and draw missles
enemMissleFire = False
if not enemMissleFire:
if enemymissle.colliderect(ship):
lives -= 1
print(f"Lives: {lives}")
enemymissle.center = (abc.centerx,abc.centery)
time.sleep(0.5)
if enemymissle.top > height:
enemymissle.center = (abc.centerx,abc.centery)
if lives == 0:
ship_kill.play()
screenControl = 3
enemymissle.move_ip(0,-missleSpeed + 1)
pygame.draw.rect(screen, WHITE, enemymissle,0)
liveval = livestext.render(f"Lives: {str(lives)}", True, WHITE)
textlives = liveval.get_rect()
textlives.center = (65,20)
screen.blit(liveval,textlives)
checkCollision(missles,smallInvaders,score,invader_kill)
checkCollision(missles,medInvaders,score,invader_kill)
checkCollision(missles,bigInvaders,score,invader_kill)
if smallInvaders == [] and medInvaders == [] and bigInvaders == []:
screenControl = 2
if screenControl == 2:
screen.fill(BLACK)
if playagn.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
print("GAME START")
screenControl = 1
lives = 3
pygame.draw.rect(screen,WHITE,(playagn),0)
if quitbutton.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
main = False
pygame.draw.rect(screen,WHITE, (quitbutton),0)
if playagn.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE,(playagn), 4)
if quitbutton.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE,(quitbutton),4)
textend = endscreen.render("YOU WON!", True, WHITE)
textrect4 = textend.get_rect()
textrect4.center = (width/2, 150)
screen.blit(textend,textrect4)
textplayagn = playagaintext.render("PLAY AGAIN", True, BLUE)
textrect5 = textplayagn.get_rect()
textrect5.center = (width/2,height/2)
screen.blit(textplayagn,textrect5)
textquit = quitword.render("QUIT",True,BLUE)
textrect3 = textquit.get_rect()
textrect3.center = (width/2,height/2+110)
screen.blit(textquit,textrect3)
if screenControl == 3:
screen.fill(BLACK)
textgameovr = gameover.render("GAME OVER", True, WHITE)
textrect6 = textgameovr.get_rect()
textrect6.center = (width/2,110)
screen.blit(textgameovr,textrect6)
if quitbutton.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
main = False
pygame.draw.rect(screen,WHITE, (quitbutton),0)
if playagn.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
print("GAME START")
screenControl = 1
lives = 3
pygame.draw.rect(screen,WHITE,(playagn),0)
if quitbutton.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE,(quitbutton),4)
if playagn.collidepoint(pygame.mouse.get_pos()):
pygame.draw.rect(screen,BLUE,(playagn), 4)
textplayagn = playagaintext.render("PLAY AGAIN", True, BLUE)
textrect5 = textplayagn.get_rect()
textrect5.center = (width/2,height/2)
screen.blit(textplayagn,textrect5)
textquit = quitword.render("QUIT",True,BLUE)
textrect3 = textquit.get_rect()
textrect3.center = (width/2,height/2+110)
screen.blit(textquit,textrect3)
pygame.display.update()
Lets say I have this table.
Config.LootboxesRewards = {
[1] = {
{name = 'a45amg', label = 'Mercedes A45 AMG ', amount = 1, type = 'car', luck = 3},
{name = '720s', label = '720s mclaren', amount = 1, type = 'car', luck = 20},
{name = 'bac2', label = 'bac2', amount = 1, type = 'car', luck = 20},
{name = 'm6prior', label = 'BMW M6', amount = 1, type = 'car', luck = 19},
{name = 'huracan', label = 'Lamborghini Huracan', amount = 1, type = 'car', luck = 19},
{name = 'yzfr6', label = 'Yamaha R6', amount = 1, type = 'car', luck = 19},
},
}
Based on that I would like to give the the player 1 item based on luck value on that table.
What is the best way to do it?
The simplest way is to index Config.LootboxesRewards[1] with math.random(#Config.LootboxesRewards[1])
This assumes that you just want to give your player a random item with uniform distribution. If you want to vary the chanc of getting particular items I suggest you start here:
https://en.wikipedia.org/wiki/Probability
Read:
https://www.lua.org/manual/5.4/manual.html#pdf-math.random
https://www.lua.org/manual/5.4/manual.html#pdf-math.randomseed
One simple solution is to put the values with a higher chance (higher luck value) more often in the loot-table than the items with a lower change (lower luck value).
You can still keep your table for convenience and pre-process the table like this:
local function gcd(a, b)
while a ~= b do
if a > b then
a = a - b
else
b = b - a
end
end
return a
end
local function gcd_tbl(values, value_getter)
if #values < 1 then
return nil
end
value_getter = value_getter or function(v) return v end
local result = value_getter(values[1])
for i = 2, #values do
result = gcd(result, value_getter(values[i]))
end
return result
end
local function process_rewards(tbl)
local result = {}
for id, rewards in pairs(tbl) do
result[id] = {}
local greatest_common_divisor = gcd_tbl(rewards, function(v) return v.luck end)
for _, reward in ipairs(rewards) do
for i = 1, reward.luck / greatest_common_divisor do
table.insert(result[id], reward)
end
end
end
return result
end
Config.LootboxesRewards = process_rewards({
[1] = {
{name = 'a45amg', label = 'Mercedes A45 AMG ', amount = 1, type = 'car', luck = 3},
{name = '720s', label = '720s mclaren', amount = 1, type = 'car', luck = 20},
{name = 'bac2', label = 'bac2', amount = 1, type = 'car', luck = 20},
{name = 'm6prior', label = 'BMW M6', amount = 1, type = 'car', luck = 19},
{name = 'huracan', label = 'Lamborghini Huracan', amount = 1, type = 'car', luck = 19},
{name = 'yzfr6', label = 'Yamaha R6', amount = 1, type = 'car', luck = 19},
}
})
You can then choose a random index from the table to find the reward:
function get_random_reward(lootbox_id)
local lootbox_rewards = Config.LootboxesRewards[lootbox_id]
if not lootbox_rewards then
return nil
end
return lootbox_rewards[math.random(#lootbox_rewards)]
end
get_random_reward(1)
EDIT:
If you wish to indicate the other way around (higher luck = less chance to drop), you could chance these 2 functions:
local function gcd_and_max_tbl(values, value_getter)
if #values < 1 then
return nil, nil
end
value_getter = value_getter or function(v) return v end
local value = value_getter(values[1])
local gcd_result, max_result = value, value
for i = 2, #values do
value = value_getter(values[i])
gcd_result = gcd(gcd_result, value)
max_result = math.max(max_result, value)
end
return gcd_result, max_result
end
local function process_rewards(tbl)
local result = {}
for id, rewards in pairs(tbl) do
result[id] = {}
local greatest_common_divisor, max_luck = gcd_and_max_tbl(rewards, function(v) return v.luck end)
local max_relevant_luck = max_luck / greatest_common_divisor
for _, reward in ipairs(rewards) do
for i = 1, max_relevant_luck - (reward.luck / greatest_common_divisor) + 1 do
table.insert(result[id], reward)
end
end
end
return result
end
if luck simply is how likely it is to be picked, so that a car with luck 10 has double chance to be picked than one with 5 you could do it like this
function pickBasedOnLuck(list)
local totalLuck = 0
for i, car in ipairs(list) do
totalLuck = totalLuck + car.luck
end
local pick = math.random(totalLuck)
for i, car in ipairs(list) do
if pick > car.luck then
pick = pick - car.luck
else
return car
end
end
end
so in your case it could be called like pickBasedOnLuck(Config.LootboxesRewards[1]).
Need help. I have my whole code here. I am trying to compare the touched object(text) to the value of a local variable answer, but it always prints nil value of text. How can I compare those two?
function scene:create( event )
local sceneGroup = self.view
local temp1 = math.random(0,10)
local temp2 = math.random(0,10)
local answer = temp1 + temp2
local background = display.newImage( "bg0.png" )
background.x = display.contentWidth*0.5
background.y = display.contentHeight*0.5
local text2 = display.newText(temp1,90,45,"Arial",55)
text2:setFillColor(0, 0, 0)
local plus = display.newText(" + ",140,45,"Arial",55)
plus:setFillColor(0, 0, 0)
local text3 = display.newText(temp2,180,45,"Arial",55)
text3:setFillColor(0, 0, 0)
local equals = display.newText(" = ",235,45,"Arial",55)
equals:setFillColor(0, 0, 0)
local secondsLeft = 02 * 60
local clockText = display.newText("02:00", 270, -7, "Arial", 35)
clockText:setFillColor(0,0,0)
local function updateTime()
secondsLeft = secondsLeft - 1
local minutes = math.floor(secondsLeft / 60)
local seconds = secondsLeft % 60
local timeDisplay = string.format("%02d:%02d", minutes, seconds)
clockText.text = timeDisplay
if timeDisplay == "00:00" then
GameOver()
print "Game Over!!!"
end
end
local countDowntimer = timer.performWithDelay(1000,updateTime,secondsLeft)
local function offscreen(self, event)
if(self.y == nil) then
return
end
if(self.y > display.contentHeight - 100) then
Runtime:removeEventListener("enterFrame", self)
self:removeSelf()
end
end
local function balloonTouched(event)
if (event.phase == "began") then
print( "object touched =", event.target )
print (answer)
if event.target == answer then
print "Good"
local temp1 = math.random(0,10)
local temp2 = math.random(0,10)
local text2 = display.newText(temp1,90,45,"Arial",55)
text2:setFillColor(0, 0, 0)
local text3 = display.newText(temp2,180,45,"Arial",55)
text3:setFillColor(0, 0, 0)
secondsLeft = secondsLeft + 5
else
print "Wrong Answer"
secondsLeft = secondsLeft - 3
end
Runtime:removeEventListener("enterFrame", event.self)
event.target:removeSelf()
end
return true
end
local function ulit()
lobo = { ("blue.png"), ("green.png"), ("red.png"), ("orange.png"), ("pink.png"), ("violet.png"), ("yellow.png") }
local lobo = display.newImage( lobo[math.random(7)], math.random(35,260), 600 )
physics.addBody( lobo, { density=0.1, friction=2.0, bounce=0.0, velocity=-40, isSensor=true } );
lobo.gravityScale = -0.1111111115
sceneGroup:insert( lobo )
lobo.enterFrame = offscreen
local text = display.newText(math.random(0,9),50,30,"Arial",40)
text.id = "sagot"
sceneGroup:insert( text )
text.enterFrame = offscreen
function lobo:enterFrame()
text.x, text.y = lobo.x, lobo.y;
end
Runtime:addEventListener( "enterFrame", lobo)
lobo:addEventListener("touch", balloonTouched)
end
timer.performWithDelay(300,ulit,0)
local backBtn = widget.newButton
{
labelColor = { default={255}, over={128} },
defaultFile= "home.png",
overFile= "home.png",
width=50, height=50,
onRelease = onBackBtnRelease
}
backBtn.x = 20
backBtn.y = -7
sceneGroup:insert( background )
sceneGroup:insert( clockText )
sceneGroup:insert( backBtn )
sceneGroup:insert( text2 )
sceneGroup:insert( plus )
sceneGroup:insert( text3 )
sceneGroup:insert( equals )
end
Try (should work)
local function ulit()
local mRandom = math.random
local imagesNames = { "blue.png", "green.png", "red.png", "orange.png", "pink.png", "violet.png", "yellow.png" }
local lobo = display.newImage( imagesNames[mRandom(7)], mRandom(35,260), 600 )
...
local n = mRandom(0, 9)
local text = display.newText(n,50,30,"Arial",40)
lobo.number = n
...
local function balloonTouched(event)
...
-- here event.target is equal lobo
if event.target.number == answer then
...
In ulit function you use the same name (lobo) for table object and diplay object. That is wrong.
I am currently working on modifying an existing function, by Ray, that checks the time elapsed and remaining:
Here is the function:
function ago(date){
var interval = "";
var offset = 0;
var result = 0;
if (isDate(arguments.date)){
var formattedDate = dateFormat(arguments.date, "dddd dd mmmm yyyy");
var k = datediff('d',arguments.date,now());
//writedump(k);
//abort;
if(k contains '-') {
if (dateDiff("s", now(), arguments.date) < 60){
// less than 1 minute show interval in seconds
offset = dateDiff("s", now(),arguments.date);
interval= offset == 1 ? "second":"seconds";
result = "#offset# #interval# left";
}else if (dateDiff("n",now(),arguments.date) < 60){
// less than 1 hour show interval in minutes
offset = dateDiff("n", now(),arguments.date);
interval= offset == 1 ? "minute":"minutes";
result = "#offset# #interval# left";
}else if (dateDiff("h",now(),arguments.date) < 24){
// less than 24 hours display interval in hours
offset = dateDiff("h", now(), arguments.date);
interval = offset == 1 ? "hour":"hours";
result = "#offset# #interval# left";
}else if (dateDiff("d",now(),arguments.date) < 2){
// less than 2 days display yesterday
result = "Tommarrow";
}else if (dateDiff("d", now(), arguments.date) < 7){
// less than 7 days display day
result = dayOfWeekAsString( dayOfWeek( arguments.date ));
}else if (dateDiff("w", now(), arguments.date)){
offset = dateDiff("w",now(), arguments.date);
interval = offset == 1 ? "week":"weeks";
result = "#offset# #interval# left";
}else if (dateDiff("m", now(), arguments.date)){
offset = dateDiff("m",now(), arguments.date);
interval = offset == 1 ? "month":"months";
result = "#offset# #interval# left";
}else if (dateDiff("yyyy", now(), arguments.date)){
offset = dateDiff("yyyy", now(), arguments.date);
interval = offset == 1 ? "year":"years";
result = "#offset# #interval# left";
}
else{
// otherwise display date
result = formattedDate;
}
}
else {
if (dateDiff("s", arguments.date, now()) < 60){
// less than 1 minute show interval in seconds
offset = dateDiff("s", arguments.date, now());
interval= offset == 1 ? "second":"seconds";
result = "#offset# #interval# ago";
}else if (dateDiff("n", arguments.date, now()) < 60){
// less than 1 hour show interval in minutes
offset = dateDiff("n", arguments.date, now());
interval= offset == 1 ? "minute":"minutes";
result = "#offset# #interval# ago";
}else if (dateDiff("h", arguments.date, now()) < 24){
// less than 24 hours display interval in hours
offset = dateDiff("h", arguments.date, now());
interval = offset == 1 ? "hour":"hours";
result = "#offset# #interval# ago";
}else if (dateDiff("d", arguments.date, now()) < 2){
// less than 2 days display yesterday
result = "yesterday";
}else if (dateDiff("d", arguments.date, now()) < 7){
// less than 7 days display day
result = dayOfWeekAsString( dayOfWeek( arguments.date ));
}else if (dateDiff("w", arguments.date, now())){
offset = dateDiff("w", arguments.date, now());
interval = offset == 1 ? "week":"weeks";
result = "#offset# #interval# ago";
}else if (dateDiff("m", arguments.date, now())){
offset = dateDiff("m", arguments.date, now());
interval = offset == 1 ? "month":"months";
result = "#offset# #interval# ago";
}else if (dateDiff("yyyy", arguments.date, now())){
offset = dateDiff("yyyy", arguments.date, now());
interval = offset == 1 ? "year":"years";
result = "#offset# #interval# ago";
}
else{
// otherwise display date
result = formattedDate;
}
}
interval = "<abbr title='" & formattedDate & "'>" & result & "</abbr>";
}
return interval;
}
The result is "1 week left" or "2 weeks ago". But rather than "2 weeks", it should show me "1 week 4 days", or whatever the days remaining are, unless it is exactly "2 weeks".
Wow that is a lot of if statements, probably better to look for a calculated approach like the following:
<cfset date1 = CreateDate(2014,07,07)>
<cfset date2 = CreateDate(2014,07,19)>
<cfset dateDifference = dateDiff("d",date1,date2)>
<cfset weeks = int(dateDifference/7)>
<cfset days = dateDifference MOD 7>
<cfoutput>
#weeks# weeks and #days# days ago
</cfoutput>
Then if you need to do it the other way around, e.g. weeks and days until, you can to that as well, just change the output text. All you need to do is make sure that date1 is always before (in time) than date2.
UPDATE
After looking at this again and based on what your where looking for I found this blog post from Steve Withington:
http://www.stephenwithington.com/blog/index.cfm/2008/8/15/Using-ColdFusion-to-Create-an-Ebayesque-Auction-Countdown-Timer-Custom-Tag
I then took his code and turned it into a function that I think should do what you want, or pretty close to it:
<cfscript>
function timeUntil(dateStart, dateEnd) {
var compareGivenDates = DateCompare(arguments.dateStart, arguments.dateEnd, "s");
var rightNow = DateFormat(now(), "mm/dd/yyyy") & " " & TimeFormat(now(), "hh:mm:ss tt");
var compareNow = DateCompare(rightNow, arguments.dateStart, "s");
switch (compareNow) {
case -1:
throw(message="End date must be after start date", detail="The end date must be after the start date otherwise the calculations will not be possible.");
case 1:
var arguments.dateStart = rightNow;
}
var returnTimeRemaining="";
var dateStart = DateFormat(arguments.dateStart, "mm/dd/yyyy") & " " & TimeFormat(arguments.dateStart, "hh:mm:ss tt");
var dateEnd = DateFormat(arguments.dateEnd, "mm/dd/yyyy") & " " & TimeFormat(arguments.dateEnd, "hh:mm:ss tt");
var hdif = Abs(DateDiff("h", dateEnd, dateStart));
var ndif = Abs(DateDiff("n", dateEnd, dateStart));
var sdif = Abs(DateDiff("s", dateEnd, dateStart));
var years2go = Abs(DateDiff("yyyy", dateEnd, dateStart));
var months2go = Abs(DateDiff("m", dateEnd, dateStart));
var weeks2go = Abs(DateDiff("ww", dateEnd, dateStart));
var days2go = Abs(DateDiff("d", dateEnd, dateStart));
if (DatePart('h', now()) LT 12 OR days2go EQ 1) {
var h = 'h';
} else {
var h = 'H';
}
var hours2go = TimeFormat(dateEnd-dateStart, h);
var min2go = TimeFormat("#dateEnd-dateStart#", "m");
var sec2go = TimeFormat("#dateEnd-dateStart#", "s");
var newmonths = months2go-(years2go*12);
var tempDate = dateadd("m", months2go, arguments.dateStart);
var newweeks = Abs(DateDiff("ww", arguments.dateEnd, tempDate));
var tempdays = Abs(DateDiff("d", arguments.dateEnd, tempDate));
var newdays = tempdays-(newweeks*7);
var comparison = DateCompare(dateStart, dateEnd, "s");
switch (comparison) {
case -1:
if (years2go GTE 1) {
returnTimeRemaining = returnTimeRemaining & "#years2go#y ";
}
if (newmonths GTE 1) {
returnTimeRemaining = returnTimeRemaining & "#newmonths#m ";
}
if (newweeks GTE 1) {
returnTimeRemaining = returnTimeRemaining & "#newweeks#w ";
}
if (newdays GTE 1) {
returnTimeRemaining = returnTimeRemaining & "#newdays#d ";
}
if (ndif GTE 60) {
returnTimeRemaining = returnTimeRemaining & "#hours2go#h ";
}
if (sdif GTE 60) {
returnTimeRemaining = returnTimeRemaining & "#min2go#m ";
}
if (sdif GTE 1) {
returnTimeRemaining = returnTimeRemaining & "#sec2go#s";
} else {
returnTimeRemaining = "0s";
}
break;
case 0:
returnTimeRemaining = false;
break;
case 1:
returnTimeRemaining = false;
break;
}
return returnTimeRemaining
}
</cfscript>
<cfoutput>
#timeUntil(CreateDateTime(2013, 7, 19, 0, 0, 0), now())#
</cfoutput>
Apart from converting it to script and making it a function, there are a few changes in here from Steve's original to get it to not return the months, weeks, days, etc... when they are 0. I've run a load of test dates through it and it appears to work ok for all the cases I have tried. Still feel there is far more code there then should be necessary but I don't have the time to spend refactoring it down.
UPDATE 2
Used this code in a programming hack session today and refactored it and improved it to the following:
<cfscript>
public string function timeDifferenceFormattedString(
required date dateStart,
date dateEnd = Now()
) {
// Is datestart actually after dateend?
switch (DateCompare(arguments.dateStart,arguments.dateEnd)) {
case 1:
var suffix = 'since';
var currentLowerDate = arguments.dateEnd;
var currentUpperDate = arguments.dateStart;
break;
case 0:
return 'The dates are the same';
break;
case -1:
var suffix = 'until';
var currentLowerDate = arguments.dateStart;
var currentUpperDate = arguments.dateEnd;
break;
}
var arrDateParts = [ 'yyyy' , 'm' , 'ww' , 'd', 'h', 'n', 's' ];
var arrDatePartsHuman = [ 'year' , 'month' , 'week' , 'day', 'hour', 'minute', 'second' ];
var arrDateDiffs = [];
for ( var i = 1; i<=ArrayLen(arrDateParts);i++ ) {
var thisDiff = Int(DateDiff( arrDateParts[i] , currentLowerDate, currentUpperDate ));
if (
thisDiff > 0
) {
arrDateDiffs.add( thisDiff & ' ' & arrDatePartsHuman[i] & ( thisDiff > 1 ? 's' : '' ) );
currentLowerDate = DateAdd( arrDateParts[i] , thisDiff, currentLowerDate );
}
}
arrDateDiffs.add(suffix);
return ArrayToList(arrDateDiffs,' ');
}
</cfscript>
<cfoutput>
<p>#timeDifferenceFormattedString(CreateDateTime(2013, 7, 10, 0, 0, 0), now())#</p>
<p>#timeDifferenceFormattedString(CreateDateTime(2013, 7, 10, 0, 0, 0), CreateDateTime(2013, 7, 10, 0, 0, 0))#</p>
<p>#timeDifferenceFormattedString(now(),CreateDateTime(2013, 7, 10, 0, 0, 0))#</p>
</cfoutput>
To take this further again you could pass in the "human" output values and suffixes as well to make the string formatting even more flexible.
i have a section that randomly spawns images from a one list and spawns them on screen and despawns them continously. the images that are spawned are put into a list called object. i trying to make is so that each image that is spawned on screen has a collision detection by the user sprite. however at the moment it only detects the very first raft that is spawned.
isOnRaft = 0
--Set log position and movement
local mRandom = math.random
local raft = {"Raft1" ,"Raft2"}
local objectTag = 0
local object = {}
function spawnlogright()
objectTag = objectTag + 1
local objIdx = mRandom(#raft)
local objName = raft[objIdx]
object[objectTag] = display.newImage(objName..".png")
object[objectTag].x = 416
object[objectTag].y = 72
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 10000, x = -96, onComplete = function(obj) obj:removeSelf(); obj = nil; end})
physics.addBody( object[objectTag], "static", {isSensor = true})
end
spawnlogright()
timer.performWithDelay(3500,spawnlogright,0)
function spawnlogright()
objectTag = objectTag + 1
local objIdx = mRandom(#raft)
local objName = raft[objIdx]
object[objectTag] = display.newImage(objName..".png")
object[objectTag].x = 416
object[objectTag].y = 168
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 10000, x = -96, onComplete = function(obj) obj:removeSelf(); obj = nil; end})
physics.addBody( object[objectTag], "static", {isSensor = true})
end
spawnlogright()
timer.performWithDelay(3500,spawnlogright,0)
function spawnlogleft()
objectTag = objectTag + 1
local objIdx = mRandom(#raft)
local objName = raft[objIdx]
object[objectTag] = display.newImage(objName..".png")
object[objectTag].x = -96
object[objectTag].y = 120
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 10000, x = 416, onComplete = function(obj) obj:removeSelf(); obj = nil; end})
physics.addBody( object[objectTag], "static", {isSensor = true})
end
spawnlogleft()
timer.performWithDelay(3500,spawnlogleft,0)
function spawnlogleft()
objectTag = objectTag + 1
local objIdx = mRandom(#raft)
local objName = raft[objIdx]
object[objectTag] = display.newImage(objName..".png")
object[objectTag].x = -96
object[objectTag].y = 216
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 10000, x = 416, onComplete = function(obj) obj:removeSelf(); obj = nil; end})
physics.addBody( object[objectTag], "static", {isSensor = true})
end
spawnlogleft()
timer.performWithDelay(3500,spawnlogleft,0)
--while the frog is on the log...
function raftCollide(event)
if ( event.phase == "began" ) then
isOnRaft = isOnRaft + 1
print(isOnLog)
elseif ( event.phase == "ended" )then
isOnRaft = isOnRaft - 1
print(isOnLog)
end
end
--add event for 'walking on the log'
object[objectTag]:addEventListener("collision", raftCollide)
so i need the user sprite to detect all of the rafts that are spawned and add 1 to isOnRaft. this will negate the water death function. is there a way to add the collision detection to all of the raft or all of the entities within the object list.
any help would be appriacaited thanks.
Just replace the last line by:
for logTag, logObject in pairs(object) do
logObject:addEventListener("collision", raftCollide)
end
Also just an advice but do what you feel like with it: Try not to declare 2 functions with the same names in the same scope... You should change the name of the second spawnRight / spawnLeft functions to have a different name :)