Closed Roguedeus closed 8 years ago
Is this an adequate benchmark? Do you see any inconsistencies? (I went a bit overboard on the class constructor though)
require "benchmark"
TIMES = 50
DATA = {}
def class_constructor(suffix)
eval(
"
class Data_#{suffix}
attr_reader :hp
attr_reader :mp
attr_reader :atk
attr_reader :def
attr_reader :mat
attr_reader :mdf
def initialize
@hp = rand(TIMES) + 1
@mp = rand(TIMES) + 1
@atk = rand(TIMES) + 1
@def = rand(TIMES) + 1
@mat = rand(TIMES) + 1
@mdf = rand(TIMES) + 1
end
end
DATA[#{suffix}] = Data_#{suffix}.new
"
)
end
x = 0
TIMES.times {class_constructor(x); x+=1} #Builds TIMES number of Data objects.
$rebuild = false
class Test
def data(var)
DATA.inject(0) {|x,(key,val)| x + val.method(var).call }
end
def data_cached(var)
return @data_cached if @prev_var == var && !$rebuild
@prev_var = var
$rebuild = false
@data_cached = DATA.inject(0) {|x,(key,val)| x + val.method(var).call }
return @data_cached
end
end
TIMES1 = 100000
x = 0
REBUILD_CHANCE = 0.25
obj = Test.new
Benchmark.bm(30) do |b|
#----------------------------------------------
b.report "Rebuild" do
TIMES1.times do
$rebuild = true if rand < REBUILD_CHANCE
x = obj.data(:hp)
end
print("#{x}")
end
#----------------------------------------------
b.report "Cached" do
TIMES1.times do
$rebuild = true if rand < REBUILD_CHANCE
x = obj.data_cached(:hp)
end
print("#{x}")
end
end
=begin
user system total real
Rebuild 1322 3.089000 0.000000 3.089000 ( 3.085176)
Cached 1322 0.811000 0.000000 0.811000 ( 0.812047)
[Finished in 4.1s]
=end
I am not too sure how to perform effective benchmarks.
I am reconsidering the nature of Armor as it is applied to battle resolution.
My previous paradigm, was type > nature where Each type applied separately, with its own strengths and weaknesses, and each nature had its own behaviors. Such as 'Active' nature being first to absorb damage and 'Passive' nature being fully restored each turn.
But now I am thinking I need something a little more intuitive and less complex, overall.
Armor.v2 is this...
Magical on top (Passive > Active) Physical on bottom (Passive > Active)
I like this... I just don't like the arbitrary dichotomy of separate 'natures' defining behavior.
I also think that it would be more intuitive if there is a single ARMOR gauge. Like there is a single HEALTH gauge, regardless of the origin of that health...
class_hp = 100
equips_hp = 100
state_hp_buff = 2.0
MHP = (class_hp + equips_hp) * state_hp_buff
MHP = 400
However, if you lose that state buff, the MHP changes to 200, but the excess Health (while it lasted) was an added buffer to damage. Though the target may have only had 250 of that 400HP remaining, when the state ends the targets HP is 200 because @hp
is capped by current @mhp
regardless of the ratio before @mhp
changed.
It can support any number of armor types, but they all accumulate into a single ARMOR total. Much like all health types accumulate into a single HEALTH total. And like losing one of those types changes the maximum health, certain conditions that invalidate an armor type, changes the maximum armor.
Also, just as there are actions that bypass DEF (invisibly) and deal direct HP loss, there will be actions that bypass ARMOR and deal direct HP loss. Only, it won't be so invisible. The player will be able to notice that an attack ignored their ARMOR.
I have to think about this some more... Perhaps write up a VERY SIMPLE Armor.v3 and do some testing.
I really REALLY need to do this for every script I write... It is helping.
I think I am in love with Armor.v3
Iteration for the win!
What steps did you take to make it easier to work with?
A complete reduction in all aspects.
Outlined class and intended functions before writing a single line.
Modular code. ;)
Somewhere along the way I forgot why this was important.
Thinking like an engineer On Aug 31, 2015 5:50 PM, "Roguedeus" notifications@github.com wrote:
_A complete reduction in all aspects. _
- Restarted with one question... Do I need that to process the simplest result?
Outlined class and intended functions before writing a single line.
- This helped me recognize redundant behavior and identify ways to simplify complex behavior.
Modular code. ;)
Somewhere along the way I forgot why this was important.
— Reply to this email directly or view it on GitHub https://github.com/Hime-Works/Requests/issues/285#issuecomment-136511177 .
Its how I approach most things that I keep at arms length... My personal projects seem to get the better of me at times.
On other news...
Did you know that release_unequippable_items
completes successfully for each empty equip slot on an actor, every time refresh occurs?
That is a lot of extra, completely unnecessary, checks. There has to be a simple way to stop this.
edit The culprit is that harmless empty slots are always equippable?(FALSE)
#--------------------------------------------------------------------------
# * Determine if Equippable
#--------------------------------------------------------------------------
def equippable?(item)
return false unless item.is_a?(RPG::EquipItem)
return false if equip_type_sealed?(item.etype_id)
return equip_wtype_ok?(item.wtype_id) if item.is_a?(RPG::Weapon)
return equip_atype_ok?(item.atype_id) if item.is_a?(RPG::Armor)
return false
end
What do you mean by completes successfully? If theres nothing in the slot it should just move on On Sep 1, 2015 8:52 PM, "Roguedeus" notifications@github.com wrote:
Its how I approach most things that I keep at arms length... My personal projects seem to get the better of me at times.
On other news...
Did you know that release_unequippable_items completes successfully for each empty equip slot on an actor, every time refresh occurs?
That is a lot of extra, completely unnecessary, checks. There has to be a simple way to stop this.
— Reply to this email directly or view it on GitHub https://github.com/Hime-Works/Requests/issues/285#issuecomment-136904451 .
A few of your scripts touch empty equip slots. Not sure why it matters but Effects Manager
freaks out if the Game_BaseItem,object
is nil...
Otherwise a simple next unless item.object
would stop trade_item_with_party
getting called a bazillion times every turn update.
And that is just ONE actor with only 4 equip slots... It would be well over 5 times that in a full party with full slots.
Every empty slot tries to trade its item.object with the party every turn, because empty slots are always unequippable.
def release_unequippable_items(item_gain = true)
loop do
last_equips = equips.dup
@equips.each_with_index do |item, i|
if !equippable?(item.object) || item.object.etype_id != equip_slots[i]
trade_item_with_party(nil, item.object) if item_gain
item.object = nil
end
end
return if equips == last_equips
end
end
I can't imagine that skipping the trade for already empty slots would break anything... But when I do it, your Effects Manager
errors in this method.
# This method basically checks the effects for all effect objects
def check_effects(objects, trigger, user=nil, item=nil)
objects.each {|obj|
type = type || Effect_Manager::Effect_Types[obj.class]
obj.effects.each {|effect|
effect_apply(user, obj, effect, type, trigger)
}
}
end
because of this check...
def check_equip_effects(equip)
super
check_effects([equip], "equip")
end
def check_unequip_effects(equip)
super
check_effects([equip], "unequip")
end
edit Or... maybe I am mistaken.
Removing the Effect Manager
stops any problems with this fix...
def release_unequippable_items(item_gain = true)
loop do
last_equips = equips.dup
@equips.each_with_index do |item, i|
next unless item.object #<------
if !equippable?(item.object) || item.object.etype_id != equip_slots[i]
trade_item_with_party(nil, item.object) if item_gain
item.object = nil
end
end
return if equips == last_equips
end
end
Can you think of a reason why my fix would cause trouble?
Seems reasonable that a trade should only be tried if there is something in the slot that needed to be unequipped in the first place.
All of those checks only occur when effect manager is in the project right? This is not an issue in the default project?
The checks occur regardless. Even in the clean install. (due to refresh)
The error for my fix only occurs with effect manager.
Would i be able to test the issue with just effect manager and your snippet On Sep 1, 2015 9:52 PM, "Roguedeus" notifications@github.com wrote:
The checks occur regardless. Even in the clean install.
The error for my fix only occurs with effect manager.
— Reply to this email directly or view it on GitHub https://github.com/Hime-Works/Requests/issues/285#issuecomment-136912955 .
Yeah. Here you go. Made a demo. http://www.roguedeus.com/Stuff/Test%20Trade%20and%20EffMngr.zip
Just walk back and forth till the 20 step on turn fires refresh.
What's interesting is that
next unless item.object
Fails, but
next unless item.object.nil?
Works.
?????
lol... I've run into a few oddities lately with ruby syntax.
Oh, I think I have an idea why that might be happening.
If you print out the object itself in the effect manager, you'll get a nil. However, if you use my snippet, you'll see that it's casual clothes.
That's pretty weird, considering that Eric's wearing casual clothes the whole time.
No, my analysis is wrong. Nil is being passed to the equip check, and that's where the script is crashing because it assumes only valid equips will be passed in.
Now the problem is, why does that happen.
We have this:
def armors
@equips.select {|item| item.is_armor? }.collect {|item| item.object }
end
First, a Game_BaseItem
object is only an armor if it actually holds an armor.
It seems like your snippet has allowed objects to pass this test.
And...here it is
def set_equip(is_weapon, item_id)
@class = is_weapon ? RPG::Weapon : RPG::Armor
@item_id = item_id
end
Game_BaseItem
assumes that if it's not a weapon, then it's an armor.
Even if has an ID of 0, which is a nil object.
This bug will be hidden when the release_unequippable_equips
call goes through because it will take all the nil equips and set them to nil appropriately.
That was quick. It likely would have taken me several hours AT MINIMUM to figure that out. I know that I couldn't see it the first time I looked. A few days ago.
So should I just use item.is_armor? || item.is_weapon?
rather than item.object
?
Use .nil? On Sep 4, 2015 12:57 PM, "Roguedeus" notifications@github.com wrote:
So should I just use item.is_armor? || item.is_weapon? rather than item.object?
— Reply to this email directly or view it on GitHub https://github.com/Hime-Works/Requests/issues/285#issuecomment-137790599 .
Ok, I'm confused.
Maybe I am misunderstanding you. But wouldn't that require me to check the opposite of nil? next if item.object.nil?
rather than next unless item.object.nil?
...
edit:
item.is_armor? || item.is_weapon?
is working. But I would rather use a more efficient solution. If for no other reason than to get use to it. The more I use it correctly, the less I have to think about it. ;)
You're right.
Well the main problem is the class being initialized to Armor because that method will always be called at the beginning.
If the class is correctly applied then it should not be an issue.
Yeah. That did strike me as really weird when I discovered the issue. It is likely why Yanfly added his equip fix to the battle engine. For the last year, every time I saw that method I went, HUH?!?
What did it do?
Presumably, it was to prevent crashing when changing classes with different equip slots. I am only guessing, since I haven't analyzed it to the end, that this wouldn't have been required if the issue you discovered didn't exist.
I wouldn't be surprised if I am wrong. ;)
#--------------------------------------------------------------------------
# alias method: force_change_equip
#--------------------------------------------------------------------------
alias game_actor_force_change_equip_aee force_change_equip
def force_change_equip(slot_id, item)
@equips[slot_id] = Game_BaseItem.new if @equips[slot_id].nil?
game_actor_force_change_equip_aee(slot_id, item)
end
#--------------------------------------------------------------------------
# alias method: weapons
#--------------------------------------------------------------------------
alias game_actor_weapons_aee weapons
def weapons
anti_crash_equips
return game_actor_weapons_aee
end
#--------------------------------------------------------------------------
# alias method: armors
#--------------------------------------------------------------------------
alias game_actor_armors_aee armors
def armors
anti_crash_equips
return game_actor_armors_aee
end
#--------------------------------------------------------------------------
# alias method: equips
#--------------------------------------------------------------------------
alias game_actor_equips_aee equips
def equips
anti_crash_equips
return game_actor_equips_aee
end
#--------------------------------------------------------------------------
# new method: equips
#--------------------------------------------------------------------------
def anti_crash_equips
for i in 0...@equips.size
next unless @equips[i].nil?
@equips[i] = Game_BaseItem.new
end
end
There may have been scripts that would explicitly delete the entire object instead of setting it to nil like this
item.object = nil
I don't understand the point of that code.
I noticed it was causing issues with some things I had done a long time ago and commented it out.
So far, its not been an issue.
My armor mechanic is mostly functional. I am resisting the urge to define to much of the advanced features I have planned before I am certain how I want them to work, but the gist of the 'core' feature set is pretty damn cool... If I am allowed to pat myself on the back. ;)
I also wrote it in less than half the space this time.
I just finished up an Attack Elements data class that will eventually become the backbone of my element damage implementation of damage delivery. Which itself is a mechanic based on 'How' damage is dealt and all the ramifications of it... Such as by bladed weapon, missile weapon, thrown weapon, effect blast, effect beam, area of effect, etc...
I think I'll write up a few blog posts, while I rest my brain and determine which is next to check off my TODO: list.
I am doing my best to design a deterministic battle hit and damage mechanic... The only 'random' aspects are the decisions made.
A battlers current state (of existence) vs. your potential action, is 100% predictable. Until it acts.
I wonder how difficult it would be to add some extra details to the help window in battle?
Currently in Yanfly's engine, enemy name (and its states/buffs) appears in the window upon selection. No reason why more info can't be added.
It is not difficult, but it also depends on what you want to add, when you want it to add, how you want it to be presented, whether it will be animated or not, etc.
I am thinking the most pertinent details will be there, as well as their effects (slightly transparent).
Such as:
But I will settle for the first two. The third can be done in time. It will be for 'Test' purposes. So it wouldn't need to be perfect. The player won't be privy to any more info than they normally get from Yanfly's enemy info script. It works great as is.
...
I still need to finish deigning this... I have some good ideas from the previous two scripts I wrote that change the way actions resolve. such as make_damage_value
and apply_item
...
At the moment I am thinking a four step process can adequately replace random chance while still maintaining a since of mystery to results.
It looks something like this... (highly abbreviated)
Core Damage(actor/class) + Damage Nature(Piercing, Crushing, Blasting) + Damage Delivery(Projectile/Melee vs. Target Mobility) + Special Attack Details(vs. Armor) + Element Rates
This will make the likelihood of being able to accurately track and predict the result difficult, especially in group vs. group conflicts, as multiple actions can wildly offset future expectations... Predictability would get better and better as battlers are reduced in number.
edit: Forgot to add Element Rates, to the damage flow.
Here is a little bit of the design...
#------------------------------------
BATTLE_FATIGUE:
Now, all battlers count their number of total maneuver that turn as
fatigue.
A battle maneuver is any execution of an offensive or defensive event,
including applying items (per repeat) and defenses like EVA and CEV.
Note: Any time EVA or CEV result in a success, a battle maneuver occurred.
Note: 'Linked' actions are only included if they are not free.
Note: 'Instant' actions cost +1 fatigue (or more).
The beginning of the next turn, restores fatigue according to the
battlers parameters. Over time, this can cause a battler to get so
fatigued that they can no longer adequately function in battle.
Note: Fatigue can be a condition in enemy action determination.
Generally, one battle maneuver, causes +1 fatigue. However, when a battler
is fighting an enemy of greater level (even enemies) the count increases
faster.
Level Difference = Count Value
# 1 +10% their difference...
+1 = 1.10
+2 = 1.20
# Half their difference...
+3 = 1.50
+4 = 2.00
+5 = 2.50
+6 = 3.00
> = +.50
The fatigue adjustment of the action.
<fatigue: formula> (Usable Item)
Note: Remember, all actions normally generate a base fatigue. This
note tag, merely adjusts that base value.
The fatigue regenerated each turn.
<fatigue_regen: formula> (Feature Object)
The action will not generate fatigue.
<no_fatigue> (Usable Item)
BTW: My armor mechanic is pretty damn sweet. I am VERY GLAD I re wrote it. The gui is still rough, as I am not yet sure how much of what info I wish to share with the player (or why). So its UGLY. But functional.
From the preliminaries, this third rewrite of the combat mechanics (not to be confused with the battle system) is going equally well. It is hard to imagine what I was thinking (or not thinking) back when I wrote the first two versions... I think I was just so intimidated by the code I couldn't get past the limited scope I was working in. That, and I still couldn't SEE where the code was going.
Just had an idea about crafting... Regarding fame, quests, and popularity.
Crafting is affected by fame?
The most rare items would require some kind of fame + quest benchmark... Maybe even a solid alignment (good or evil, etc...), when you are talking godlike equipment.
Want to craft a Legendary Sword? You need to have completed at least one Legendary Quest line, have a Fame of 'X' or greater... Plus, collect all the materials and the skill to craft it.
Without meeting the prerequisites, the player would have a cap of a certain item rarity in their recipe list.
Sounds like something that would be easy to implement with variables and switches.
Yeah. I was thinking the same thing. The crux is having a good crafting script.
Of course, there are a few details specific to my play philosophy and chosen mechanics that will likely be best with a plugin of some sort.
Hmm, what would you consider to be "good" crafting script? I see a crafting system as follows
Naturally, that's just a vague description of crafting.
We have Element Damage... And
UsableItem
Effects, like add state, etc... But nothing combined.What if, a stun wasn't just a stun, but an ICE stun? All rates are effected first by element resistance, and then by effect resistance. Thus a target 50% resistance to ICE, and 50% Resistance to Stun, has a 25% total resistance against ICE Stun.
This is just a simple example. But it would make certain effects able to be applied much more liberally, when something Immune to an element, is by extension also immune to all that elements linked effects.
Meanwhile, an EARTH Stun, will work fine... etc.
Elements are purely cosmetic. It would be linked to element ID's and thus can be easily extended.
Now... How would the easiest way to implement it be?
If the effect is a state, the note tag could be on that state... But then, it would require every state of every element to be individually defined. What if the link were made on the
UsableItem
?Thus, only one state is needed, and can be applied under different circumstances. Kind of like conditional effects.
This would mean that each blind effect could be assigned a different element ID dependency. Or the same dependency, under different conditions... etc...