NerdanelVampire: ToME 3 has moved to a combat style separation between melee and archery. Sure, that's the realistic way, but Zothiqband has a gamist design philosophy. I would like to stick to the old Angband way of firing with f and meleeing by bumping into a monster, all in one mode. It doesn't seem that easy to do, though... It seems I'm in for forking engine subsystems and doing major modifications to them.
(There is also a problem with characters with no barehanded skill being able to switch to barehanded combat and triggering a nasty freeze bug when they try to hit something)
Ideally, I would like to get rid of the combat style selection altogether and have it depend solely on whether the player is wielding a melee weapon or not.
BucketMan: Both Dragonball T and Zombie Horror do exactly what you're describing. The modifications don't need to be major. Just copy the engine subsystems, change the name, load them yourself and make the changes you want to make. Yes, 'forking' as you call it. But you probably only need to change two or three lines to do what you want to do.
NerdanelVampire: The modifications will indeed need to be major. I'm currently trying to make archery stop being a combat mode. There is a bug somewhere that makes the T-Engine crash silently... I'm not sure if even it's possible for the new archery to depend on the engine code it used to as a combat mode.
BucketMan: If you can give me a specific question, I might be able to answer it. Again, both Dragonball T and Zombie Horror use the default "combat" subsystem, but neither of them have combat style switching despite having weapons, barehand and ranged attacks. Basically all you have to do is use add_combat_system only ONCE. If you look at Zombie Horror, for example, in scripts/combat.lua, you'll see only a single:
constant("COMBAT_WEAPON", add_combat_system
entry, and that entry handles all combat forms, regardless of what the player is using to do damage. It's called COMBAT_WEAPON simply because I copied it from Dragonball T. The name isn't important, only that only a single instance of add_combat_subsystem is used. If the engine only sees one combat subsystem, it won't feel the need to do any switching. But if you load combat_achery, combat_weapon_default and combat_barehand_default separately, the engine sees three subsystems and engages in switching.
NerdanelVampire: It looks like I have quite a bit of copy-pasting ahead of me... I'll have to merge four long files plus a bit from DBT. I hope I don't get swamped by the lines of code.
BucketMan: You say it like it's such a terrible burden! :P It may help if you keep in mind that it doesn't really matter where most of the code is. You don't have to squeeze everything into the "attack =" entry. You could for example, copy the various combat subsystems you want as complete and whole files, make the necessary changes, and then refer to them as needed. Also...it might be easier to copy from Zombie Horror than Dragonball T. The DBT combat script it very old, and was made long before I had the slightest clue what I was doing. It works, but the code is neither clean nor intuitive. Zombie Horror might be easier to copy from.
NerdanelVampire: After a couple of false starts I finally decided on the structured method of first merging the code in the subsystem calls with the actual subsystems. Now I need to get all of it together somehow, and I just noticed the subsystem that's used for fighting with possessed monsters, and I need to decide whether it should be merged too...
NerdanelVampire: ARGH!!! I moved everything into subsystems only to find out that you can't define the relevant skill in a subsystem for some mysterious reason. (Or maybe you can, but I couldn't figure out how.) So then I moved all the base_skill_name entries back into combat.lua (and possession.lua) and now the game crashes on startup without a single error message. gdb tells me there is a segmentation fault. I'm going to go file a bug report with the backtrace but that isn't going to help me make fix the combat styles.
NerdanelVampire: Fourth time's the charm. Yes, this is indeed the fourth time I've started the copy-paste operation from scratch. The good news is that I found out why the third time didn't work. I had overlooked some code that when other code was changed triggered a particularly nasty, confusing, and elusive crash bug. Now I have the subsystems as four working units and I have to start looking how to make the four into one or two.
NerdanelVampire: Would anyone know how to check if INVEN_MAINHAND and INVEN_OFFHAND are empty or not? This is supposed to determine whether barehanded or weapon combat is used. (I don't want the player to be able to use the bow by bumping into monsters.)
NerdanelVampire: Answering to my previous question, I think get_object(INVEN_MAINHAND) does it, or at least it causes no error messages. I just managed to kill the first monster with my new combat subsystem so things should work at least partially. The effort needed has been far from a few lines, and I still have done nothing to include fighting in possessed bodies. And yes, I'm spending my Christmas Eve programming...
NerdanelVampire: I've been spending my Christmas Day programming too. At the moment barehanded doesn't do anything but wastes a turn, weapon combat works on a basic level but specific masteries do nothing (although I wonder if they ever did in ToME 3 and I simply never noticed), and I haven't tested archery yet.
NerdanelVampire: I found out that get_item(INVEN_MAINHAND) doesn't always return nil when nothing is wielded. ARGH! How can I do this simple check?
BucketMan: Try: player.inventory[INVEN_MAINHAND][n], where n is the slot number of the inventory slot you want. For Zothiq, this will be 1 for everything except rings, which you have two of. For example:
local mainhand, offhand if player.inventory[INVEN_MAINHAND][1] then -- I have something in my main hand. mainhand = player.inventory[INVEN_MAINHAND][1] elseif player.inventory[INVEN_OFFHAND][1] then -- I have something in my off hand. offhand = player.inventory[INVEN_OFFHAND][1] end
I'm not sure how you'll want to handle it from there. Just because something is in your hand, doesn't necessarily mean it's a weapon. I see you have a LITE slot in Zothiq, but I don't know if you allow weapons in the off hand.
NerdanelVampire: That works.
For some reason however, player.inventory[INVEN_MAINHAND] doesn't exist at the start of the game, so I had to do it like that:
combat.zothiq.get_mode = function()
if player.inventory[INVEN_MAINHAND] then
if(player.inventory[INVEN_MAINHAND][1] or player.inventory[INVEN_OFFHAND][1]) then
combat.zothiq.mode = SKILL_MASTERY
else
combat.zothiq.mode = SKILL_HAND
end
end
endI have no idea what isn't getting initialized (I have slots.lua before combat.lua), and so I'm calling get_mode at the start of every attack and for the C screen, which should also help keep the archery mode straight. Now to trying to find out why archery doesn't work...
NerdanelVampire: Good news! I fixed the longstanding untrained barehanded lockup, and also reported the solution in bugs.t-o-m-e.net. Now I need to look into why the C screen claims I never have any ammo when I clearly do and why the submasteries don't do anything.
NerdanelVampire: I found out that skills like swordmastery had NEVER been of any use in Zothiqband. Well, they are now. Even with the ammo display bug, Zothiqband combat is now better than it has ever been!
NerdanelVampire: Well, I noticed that the submasteries had actually been there all along, except when I failed to copypaste the correct piece of hook code in for weapons, but my change made the C screen show them properly for the first time (I think). Global variables are so hard to keep track of...
NerdanelVampire: I'm mystified by the following code, which is nearly straight from the engine subsystem. In particular, I don't understand why o_ptr always ends up nil, resulting the C screen showing "no ammo" when I demonstratably have ammo and am even capable of killing monsters with archery. Ammo slot or in the backpack, it matters not.
item = compute_slot(INVEN_AMMO, 0)
o_ptr = get_object(item)
-- If nothing correct try to choose from the backpack
if ((not o_ptr) or (get_flag(j_ptr, FLAG_AMMO) ~= o_ptr.tval)) then
return "no ammo"
end
-- Get the "ammo"
o_ptr = get_object(INVEN_AMMO)NerdanelVampire: Okay, I solved that one by myself. My next question is, how do you get dies and sides from an item? The archery code has o_ptr.dd and o_ptr.ds respectively but those return 0.
BucketMan: Depends completely on how you're storing damage information in items. If you want to use the standard, obtuse method used by the engine, in which damage information is all stored in a single flag, like this:
DAM = getter.damages{CRUSH = {2,10}}
Then you can retrieve the various pieces of information like this:
local dams = player.inventory[INVEN_WIELD][which].flags[FLAG_DAM] local typ = flag_max_key(dams) local wp_dam = to_dam + rng(flag_get(dams, typ), flag_get2(dams, typ))
The above is from DBT. But this isn't the only way to store damage information. For example, in Zombie Horror I didn't want to deal with this sort of obscurity, so I store each piece of damage information in it's own flag, which amazingly simplifies retrieving it. Here's a typical set of weapon flags:
Z_WEAPON = true Z_DAM_TYPE = dam.MELEE Z_DAM_MIN = 1 Z_DAM_MAX = 2 Z_WEAPON_HIT = "stab " Z_WEAPON_MISS = "stab at "
And to retrieve any of it, I use the standard obj.flag[FLAG_NAME] format to get what i want. For example:
min_dam = obj.flags[FLAG_Z_DAM_MIN] max_dam = obj.flags[FLAG_Z_DAM_MAX] dam_typ = obj.flags[FLAG_Z_DAM_TYPE]
Which is amazingly easier, more intuitive to look at and more difficult to break. If I were to rebuild DBT combat, I'd do it this way. It's less efficient, but honestly...so what? Nobody is going to be killed by an extra 2k item file. Of course, this does also limit weapons to only ever doing a single type of damage, whereas the ToME module method allows weapons to do any number of types of damage. It would also require special handling to be friendly with the default engine BRAND system. But, I didn't particuarly like that either. In any case, lots of options. Since Zothiq handles damage data storage the same way as ToME, you should be able to simply copy and paste the first example from Dragonball T. But don't feel the need to always do things the way the engine does. Sometimes it's great, sometimes it's not. And as you noted, sometimes it simply doesn't work. In general I'd say that the default engine and ToME module ways of doing things are a lot like flying a plane vs. driving a car. A plane is faster, can go farther, is more efficient, etc. But if you just want to drive to the grocery store down the street it's a lot more practical to use the car instead, and you'll save a lot of time in the process.
NerdanelVampire: Great! Now all the normal combat types appear to work as intended as far as I can tell.
I could release 0.7.1 right now, but I think I'll try to merge fighting in possessed bodies too.
I think I just saw first hand one of the problems of script languages like Lua - things can change at one point in the code and break some obscure portion elsewhere, and nothing will get noticed unless the discrepancy causes an error message, which won't happen if the interpreted decides to think the broken reference is just another uninitialized variable, which it defaults to nil...
NerdanelVampire: I've been thinking... I've never played a possessor, so I'd like some opinions on how to design the combat in regards to them. I'm not sure that I should remove the explicit choice of a combat style for them. Namely, what to do with monsters that have both hands and innate attacks? I have been having weapons in hands -> weapon combat, and empty hands -> barehand combat. Monsters' innate blows confuse things. Since Zothiqband is an asymmetric game, monsters don't normally do as much damage as a player but have more hitpoints, which makes innate blows less useful, but if I added a skill bonus to the blows... The other way would be to use weapon/barehanded if at all possible and monster blows if not.
BucketMan If it were me, I'd apply a flag to individual monsters to identify how they should be treated. If a player possesses the body of a hydra, he probably shouldn't be allowed to use 'barehand' combat at all. His skill shouldn't apply. If a player possesses something humanoid, he shouldn't necessarily pick up monster combat abilities. A grandmaster mystic does melee damage because he has skill in fighting, not claws and teeth. In cases like this, barehand skill should determine combat, with no 'monster combat' as an option. You might also consider giving posessors a 'monster combat' skill which they can use to enhance their melee attacks when fighting in non-humanoid forms. Somebody used to a human body isn't instantly going to be as effective with tooth and claw as something that's used such a body all its life.
NerdanelVampire: This is my current combat mode switching code. Before I added the player race check, it would never choose possession mode outside of the game initialization phase. Now it never chooses weapon combat or barehanded when possessing monsters.
combat.zothiq.get_mode = function()
if player.inventory[INVEN_MAINHAND] and (player.body_monster == RACE_PLAYER) then
if(player.inventory[INVEN_MAINHAND][1] or player.inventory[INVEN_OFFHAND][1]) then
combat.zothiq.mode = SKILL_MASTERY
else
combat.zothiq.mode = SKILL_HAND
end
else
combat.zothiq.mode = SKILL_POSSESSION
end
endI guess I could add a new flag to all monsters that have hands, but I think there should be a better and less error-prone way of doing this... How can I check for the presence of hands (or the humanoid body type, since only it currently has hands)?
BucketMan: Unfortunately you're delving into things I'm less familiar with. None of my modules use possession or monster bodies in any way. However...I think the problem might be that:
(player.body_monster == RACE_PLAYER)
should read:
(player.body_monster.r_idx == RACE_PLAYER)
As to checking for the presence of hands on the monster body, again...I've never done it, but if I were to start guessing, I might trying something like this:
local race = r_info[player.body_monster.r_idx + 1] local foo = race.body[MAINHAND] if foo and foo > 0 then -- We have a primary hand else -- We do not end
But this is just a wild stab in the dark. Again, I've never actually worked with possession or monster bodies. By the way, have you seen the forum post from DarkGod? We will have an alpha18 soon.
NerdanelVampire: I see I had indeed made a mistake leaving out that .r_idx. Otherwise, I still am not getting the hand detection to work. Perhaps I will release a version for alpha18 (yes, I saw that post) that will only allow monster-form fighting for possessors.
NerdanelVampire: Well, Zothiqband 0.7.1 is now available with unified combat and without hand-detection while alpha18 is not.
ToME Wiki