I've been trying to take a module in Lua and use it to imitate an object. i.e. I made a deck of cards:
local Card = require("Card")
local function firstCard()
Card.newDeck()
Card.drawCard()
return Card.getCard()
end
local function secondCard()
Card.newDeck()
Card.drawCard()
return Card.getCard()
end
first = firstCard()
second = secondCard()
print(first)
print(second)
I set first = firstCard() and second = secondCard() but when I print the two variables second occasionally results as nil. I'm honestly lost. Here's the actual module itself.
local Card = {}
local deck
local value
local number, suit
local index = 0
local getCard
local getValue
function Card.newDeck()
deck = {}
value = {}
for x = 1, 13 do
if x == 1 then
number = "Ace"
elseif x == 11 then
number = "Jack"
elseif x == 12 then
number = "Queen"
elseif x == 13 then
number = "King"
else
number = x
end
for x1 = 1, 4 do
if x1 == 1 then
suit = "Clubs"
elseif x1 == 2 then
suit = "Diamonds"
elseif x1 == 3 then
suit = "Hearts"
else
suit = "Spades"
end
index = index + 1
deck[index] = number.." of "..suit
value[index] = x
end
end
end
function Card.drawCard()
index = math.random(52)
getCard = deck[index]
getValue = value[index]
end
function Card.getCard()
return getCard
end
function Card.getValue()
return getValue
end
function Card.getIndex()
return index
end
return Card
I have limited knowledge of Lua when it comes to Object-Oriented programming and to be honest, I typically only use it for calculations or small games to keep me occupied I'm class- I'm only 16. I'm more used to Java, even though I started using Lua well before I picked up Java. I just want to know if and how I can make this work. For the most part it works, just those occasional nil values.
The problem is that you have declared the index variable local at the top level of your module. That means that the random value of index that you have calculated in your first drawCard() is reused in your second call to newDeck(). You can add print(index) at the start of newDeck() to see what I mean.
There are several ways to solve the problem. One would be to add index = 0 at the top of newDeck(). A better one would be to declare your variables with smaller scoping, i.e. make index local to each function that uses it.
Try to use this instead, seems to work fine, it should print "ERROR NO VALUE" if there is some issue with the index, if that occurs (it shouldn't though), just print the index (in the generateCard() function).
This is the test
local Card = require("Card");
local function firstCard()
Card.newDeck()
return Card.generateCard(); -- two parameters, deck and value
end
local function secondCard()
Card.newDeck()
return Card.generateCard();
end
first = firstCard()
second = secondCard()
print(first)
print(second)
This is the module
local Card = {deck={};value={};};
function Card.newDeck()
local self = Card
local deck = self.deck;
local value = self.value;
local cards = {[1]="Ace",[11]="Jack",[12]="Queen",[13]="King"};
local types = {[1]="Clubs",[2]="Diamonds",[3]="Hearts",[4]="Spades"};
for x = 1, 13 do
for i = 1, 4 do
local _card,_type=(cards[x] or x),types[i];
deck[#deck+1] = _card.." of ".._type
value[#deck+1] = x
end
end
end
function Card.generateCard()
local self = Card;
if(not math.round) then
math.round = function(value) local mi,ma=math.floor(value),math.ceil(value)return(mi+0.5>value and mi or ma)end;
end
local index = math.round(math.random(1000, 52000)/1000);
return (self.deck[index] or "NO VALUE FOR CARD"),(self.value[index] or "NO DECK FOR CARD");
end
return Card
Related
I am currently trying to implement some different actions instead of attack, but I want to focus on a party function, that looks like this, I can figure out the types of actions I want but I want to change the battle function to fit into a role that can accept multiple actions and not just attack
func change_party(inventory,index): # Inventory being if enemy or player
if inventory[index].fainted == false: # Checks if the monster is not dead
var hold = inventory[0] # Holds the first slot and then applys it later to the selected slot
inventory[0] = inventory[index]
inventory[index] = hold
This changes the index that was inputed by buttons and swaps it around with the first slot in the array this is because the first slot is what monster is shown first, I also have this battle function:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for attacker in participants:
var player_move = player_inventory[0].move_slot[action_index]
var random_move = randi() % 3 + 1
var enemy_move = attacker.move_slot[0] # Use random_move currently 0
var target = _choose_target(attacker, participants)
if attacker == player_inventory[0]:
yield(attack(attacker, target, player_move), "completed")
else:
yield(attack(attacker, target, enemy_move), "completed")
if player_inventory[0].current_hp <= 0:
player_inventory[0].current_hp = 0
player_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.LOST
return
if enemy_inventory[0].current_hp <= 0:
enemy_inventory[0].current_hp = 0
enemy_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.WON
return
self.current_state = GameState.ACTION
One solution that came to me was trying to have all the actions in a array and just call the action I want based on input but I have no clue how I would make that look readable or bug-free
In this function is how it decides who´s turn it is based on speed, but sometimes I want the player to go first for example when I want to change party members and when the player has changed I want the enemy to start attacking, But I am scratching my head on how would I make it change actions, I know the attack function should be changed if I want it to do something else but I also want to be able to control who´s turn based on what type of action is used, I am sorry if I didnt explain it well, hope you guys understand, I don't want to repeat my self by making another similar to battle function so how do I avoid being repetitive while also doing what I want?
BattleScript:
func attack(attacker, target, move):
print(str(attacker.name) + " used " + str(move.name) + " " + str((move)))
var new_text = (attacker.name + " attacked with " + move.name)
text_scroller(new_text)
var data = recieve_damage(move,attacker,target)
#var data = target.take_damage(move,attacker) # Calls the function to take damage
yield(get_tree().create_timer(2), "timeout") #Wait for 2 seconds
Battle_Ui_Updater()
if data:
yield(critical_hit(attacker),"completed")
func critical_hit(attacker):
var new_text = (attacker.name + " has landed a critical hit! ")
text_scroller(new_text)
print(attacker.name + " has landed a critical hit!")
yield(get_tree().create_timer(2.5), "timeout")
func Get_effectiveness(attack_type,defence_type):
if attack_type == TypeData.types.none or defence_type == TypeData.types.none:
return 1
print("row : " + str(attack_type))
print("col : " + str(defence_type))
return TypeData.chart[attack_type][defence_type]
func recieve_damage(action,attacker,defender):
var critical = 1
var critical_chance = randi() % 100 + 1
if critical_chance <= 6.25:
critical = 2
var attack_mode
var defence_mode
if action.is_special == true:
attack_mode = attacker.special_attack
defence_mode = defender.special_defence
else:
attack_mode = attacker.attack
defence_mode = defender.defence
var type : float = Get_effectiveness(action.type,defender.type_1) * Get_effectiveness(action.type,defender.type_2)
var modifier : float = rand_range(0.85,1.0) * type * critical
var a : float = (2.0 * attacker.level / 5.0 + 2.0)
var b : float = (a * attack_mode * action.power / defence_mode) / 50.0
var c : float = (b + 2.0) * modifier
var damage = int(c)
defender.current_hp -= damage
print(str(attacker.name) + " = " + str(damage))
return critical > 1
Swap Party member with the current one:
func change_party(inventory,index):
if inventory[index].fainted == false:
var hold = inventory[0]
inventory[0] = inventory[index]
inventory[index] = hold
print(inventory[0].hp)
Battle_Ui_Updater()
move_ui_updater(player_inventory[0])
yield(get_tree().create_timer(2),"timeout")
I came up with this:
class_name Task
class Skipper:
signal skip
func emit() -> void:
emit_signal("skip")
var _instance:Object
var _method:String
var _parameters:Array
var _result = null
func _init(instance:Object, method:String, parameters:Array = []) -> void:
_instance = instance
_method = method
_parameters = parameters
func execute():
var instance = _instance
var parameters = _parameters
if instance != null and instance.has_method(_method):
_instance = null
_parameters = []
_result = instance.callv(_method, parameters)
if _result is GDScriptFunctionState && _result.is_valid():
_result = yield(_result, "completed")
return _result
var skipper = Skipper.new()
skipper.call_deferred("emit")
yield(skipper, "skip")
return _result
And this is how you initialize it:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = Task.new(self, "attack", parameters)
And this is how you use it:
yield(player_action.execute(), "completed")
The new thing about this class is that it will be asynchronous regardless of whether the method it calls is asynchronous or not (so you don't have to worry if what it calls yields or not). And it will complete after the method it calls completes, either way. And it even returns (null if what it calls does not return)!
Note: This code will memoize the result, and get rid of the parameters and instance it was linked to. Subsequent calls to execute will simply return the memoized result. This way it does not hold references unecesarily.
How do we do that?
Well, first of all, we are using callv to call a method by name with an array of parameters. There is a class FuncRef in Godot that can be used in similar fashion. However, not using it resulted more convinient.
Second, calling something asynchronous would be yield(..., "completed") but we don't know if what we are calling is asynchronous. So we check. How? If it is asynchronous, it actually returns the state it left the execution as a GDScriptFunctionState, so we check that.
If it is GDScriptFunctionState, then we can use yield(..., "completed") on it.
If it isn't. We need to somehow make the function asynchronous. We are going to do that by emitting a signal with call_deferred. I decided in making it an inner class so that it is not exposed outside of the script.
Also note that the code checks if the instance is not null and has the method passed. But if it does not, it will simply return the result it had stored. This is part of the memoization mechanism, however, it also means that you have no feedback if you passed the wrong instance of wrote the method name wrong.
Finally, it should work with this version of _battle:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for current_participant in participants:
if current_participant == player_inventory[0]:
yield(player_action.execute(), "completed")
else:
yield(enemy_action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION
func decide_battle_state():
if check_fainted(player_inventory[0]):
return GameState.LOST
if check_fainted(enemy_inventory[0]):
return GameState.WON
return GameState.BATTLE
func check_fainted(participant) -> bool:
if participant.current_hp > 0:
return false
participant.current_hp = 0
participant.fainted = true
Battle_Ui_Updater()
return true
You could make an Task with speed:
class_name BattleAction extends Task
# warning-ignore:unused_class_variable
var speed
func _init(instance:Object, method:String, parameters:Array = []).(instance, method, parameters) -> void:
pass
Then use like this:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = BattleAction.new(self, "attack", parameters)
player_action.speed = player_inventory[0].speed
And finally _battle can look like this:
With your BattleAction which has speed, you can do this:
func _battle():
actions = [player_action, enemy_action]
actions.sort_custom(self, "check_speed")
for action in actions:
yield(action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION
I am having trouble achieving an expected output. I am trying to create a byte adder using logical operators such as AND, XOR and OR. I have taken the minimal code required to reproduce the problem out of code, so assume that finalfirstvalue = "1010" and finalsecondvalue = "0101".
secondvalueindex = (len(finalsecondvalue) - 1)
carry, finalans = False, []
for i in range(-1, -len(finalfirstvalue) - 1, -1):
andone = (bool(finalfirstvalue[i])) & (bool(finalsecondvalue[secondvalueindex]))
xorone = (bool(finalfirstvalue[i])) ^ (bool(finalsecondvalue[secondvalueindex]))
andtwo = (bool(carry)) & (bool(xorone))
xortwo = (bool(carry)) ^ (bool(xorone))
orone = (bool(andone)) | (bool(andtwo))
carry = (bool(orone))
finalans.append(xortwo)
secondvalueindex -= 1
answer = ''.join(str(e) for e in finalans)
print (answer)
Actual Output: FalseTrueTrueTrue
Expected Output: TrueTrueTrueTrue
The code then follows to change back into zeroes and ones.
Because its missing a single boolean I feel like the issue is with my indexing. Although I've played around with it a bit and not had any luck.
I need to carry out these operations on the two variables mentioned at the start, but for the right most elements, and then move to the left by one for the next loop and so on.
First mistake is You are representing your binary numbers as string values.
finalfirstvalue = "1010"
finalsecondvalue = "0101"
secondvalueindex = (len(finalsecondvalue) - 1) == 3
So in second for loop you will get the result as
(finalsecondvalue[secondvalueindex]) == '0'
If you check in your Idle
>>> bool('0')
True
>>>
Because '0' is not actual 0 it is an non-empty string so it return True.
You need to cast your result to int before checking them with bool
Like this
(bool(int(finalsecondvalue[secondvalueindex])))
EDIT 2 Adding with variable lenghts
Full adder with verification using bin() function
a="011101"
b="011110"
if a>b:
b=b.zfill(len(a))
if a<b:
a=a.zfill(len(b))
finalfirstvalue = a
finalsecondvalue = b
carry, finalans = 0, []
secondvalueindex = (len(finalsecondvalue))
for i in reversed(range(0, len(finalfirstvalue))):
xorone = (bool(int(finalfirstvalue[i]))) ^ (bool(int(finalsecondvalue[i])))
andone = (bool(int(finalfirstvalue[i]))) & (bool(int(finalsecondvalue[i])))
xortwo = (carry) ^ (xorone)
andtwo = (carry) & (xorone)
orone = (andone) | (andtwo)
carry = (orone)
finalans.append(xortwo)
finalans.reverse()
answer=(''.join(str(e) for e in finalans))
print(str(carry)+answer)
print(bin(int(a,2) + int(b,2))) #verification
So I found the issue was to do with carry. I changed my code to look like the following. Prior to this code below, is code to convert binary values to boolean. For instance, all ones will equal True and all zeroes will equal False.
carry, finalans = False, []
indexvalue = (len(finalfirstvalue)-1)
while indexvalue >= 0:
andone = (firstvaluelist[indexvalue]) & (secondvaluelist[indexvalue])
xorone = (firstvaluelist[indexvalue]) ^ (secondvaluelist[indexvalue])
andtwo = (carry) & (xorone)
xortwo = (carry) ^ (xorone)
orone = (andone) | (andtwo)
carry = (orone)
if (carry == True) & (indexvalue == 0):
finalans.append(xortwo)
finalans.append(True)
else:
finalans.append(xortwo)
indexvalue -= 1
for n, i in enumerate(finalans):
if i == False:
finalans[n] = "0"
if i == True:
finalans[n] = "1"
finalans.reverse()
answer = ''.join(str(e) for e in finalans)
print (answer)
So if there was a single value missing, it was still stored in carry from the final loop but did not get the opportunity to be appended to the final result. To fix this, I added in an if statement to check if carry is containing anything (True) and if the loop is on its final loop by checking if indexvalue is at 0. This way, if the inputs are 32 and 32, rather than getting [False, False, False, False, False, False] as the output, the newly entered if statement will add the missing value in.
Gadfly has a nifty macro that generates a copy constructor and optional argument constructor for some large types:
# Generate large types where each field has a default value to which it's
# initialized.
macro varset(name::Symbol, table)
#assert table.head == :block
table = table.args
names = Any[]
vars = Any[]
parameters = Any[]
parameters_expr = Expr(:parameters)
for row in table
if isa(row, Expr) && row.head == :line
continue
end
if isa(row, Symbol)
var = row
typ = :Any
default = :nothing
elseif row.head == :tuple
#assert 2 <= length(row.args) <= 3
var = row.args[1]
typ = row.args[2]
default = length(row.args) > 2 ? row.args[3] : :nothing
else
error("Bad varset systax")
end
push!(names, var)
push!(vars, :($(var)::$(typ)))
push!(parameters, Expr(:kw, var, default))
parameters_expr = Expr(:parameters, parameters...)
end
new_with_defaults = Expr(:call, :new, names...)
ex =
quote
type $(name)
$(vars...)
function $(name)($(parameters_expr))
$(new_with_defaults)
end
# shallow copy constructor
function $(name)(a::$(name))
b = new()
for name in fieldnames($(name))
setfield!(b, name, getfield(a, name))
end
b
end
end
function copy(a::$(name))
$(name)(a)
end
end
esc(ex)
end
I would like to add a partial copy constructor that lets you provide an object and some partial parameters. It would copy the provided object and overwrite only the fields that you give it.
How could I define something like the following? Pseudocode-ish:
function $(name)(a::$(name), $(parameters_expr))
b = new(a)
for name in names
if getfield(b, name) != default
setfield!(b, name, new_val)
end
end
end
Below is code to encode an integer value into an ASCII string. It is written in Python, and works fine from my testings.
def encode(value):
code = ''
while value%254 != value:
code = code + chr(value%254)
value = value/254
code = code + chr(value)
return code
def decode(code):
value = 0
length = len(code)
for i in range(0, length):
print code[i]
value = value * 254 + ord(code[length-1-i])
return value
code = encode(123456567)
print code
print decode(code)
However when I try the same implementation in Lua, the values encoded and decoded do not match up. Here is my Lua version:
function encode(value)
code = ''
while value%254 ~= value do
code = code .. string.char(value%254)
value = value/254
end
code = code .. string.char(value)
return code
end
function decode(code)
value = 0
code = string.reverse(code)
for i=1, #code do
local c = code:sub(i,i)
print(c)
value = value*254 + string.byte(c)
end
return value
end
code = encode(2555456)
print(decode(code))
Please note that I am trying to using mod 254 so that I can used 255 as a delimiter.
Use local whenever you are creating variables with similar names (for eg. code and value in your code).
When you use value = value / 254, you need to take only the integer part of the division and not the entire number.
Therefore:
function encode(value)
local code = ''
while value % 254 ~= value do
code = code .. string.char( value % 254 )
value = math.floor( value / 254 )
end
code = code .. string.char( value )
return code
end
function decode(code)
local value = 0
code = code:reverse()
for i = 1, #code do
local c = code:sub( i, i )
value = value * 254 + c:byte()
end
return value
end
I have random spawned objects that automatically moves across the screen. I want it so that when the objects reach a certain x position it despawns itself.
local mRandom = math.random
local objects = {"Vehicle11" ,"Vehicle21","Vehicle31","Vehicle41"}
local objectTag = 0
local object = {}
local function spawncarright()
local rightcar = {408,312}
objectTag = objectTag + 1
local objIdx = mRandom(#objects)
local objName = objects[objIdx]
object[objectTag] = display.newImage(objName..".png") -- see the difference here
object[objectTag].x = 32
object[objectTag].y = rightcar[math.random(1,2)]
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 3500, x = 348})
end
timer.performWithDelay(2000,spawncarright,0)
so once reaches object[objectTag].x = 348 the object despawn
Try this:
local function deSpawn()
for i=1,objectTag do
if(object[i]~=nil and object[i].x~=nil and object[i].x>=348)then
-- If you want to remove the object, then use the following 2 lines --
object[i]:removeSelf()
print("Removed object["..i.."]")
--or else if you want to reposition the object, then uncomment the following --
--[[
spawncarright()
--]]
end
end
end
Runtime:addEventListener("enterFrame",deSpawn)
Keep coding................... :)
You should do it within the transition.to call:
object[objectTag].deleteSelf = function(self)
object[self.name] = nil -- Remove reference to object in table
display.remove(self)
self = nil
end
local localObj = object[objectTag] -- Do this so the object doesn't change with the objectTag does; if objectTag is incremented, then when the transition ends, it won't be pointing to the same object when we call the function
transition.to(localObj, {time = 3500, x = 348, onComplete = function() localObj:deleteSelf() end})