My take on "A Dark Stormy Knight" adventure

Discussion in 'General Modification' started by Mellonril, Jan 9, 2023.

Remove all ads!
  1. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    Hi! I am learning how to mod ToEE using Co8 and Temple+.
    I've started by studying the other mods, particularly those developed by Anatoliy.
    I chose the Dark Stormy Knight adventure as it is small in size and has few NPCs/dialogues.
    I've done some initial testing using the tutorial maps to find the correct camera angles and sizes for the new maps.
    The background graphics are rendered using the Blender game engine, then split and recomposed on top of one of the existing maps.
    For now, only the Hightower tomb is in development, but I'd like to add the exterior maps as well.

    I'd like to use this forum post as a development journal and to ask any quick questions about modding.

    At the moment the party will enter the tomb through the main entrance.
    The main hall has three passages, two of which are closed by barred doors. The passage on the right leads to the chamber with the stone chest inside. After some time, a rat swarm will break through the doors leading to the main hall.
    Once the party has killed the rats, they can proceed to the other corridors.

    Here's a quick video about the first room:


    The scripting part is a series of timevents to play the sounds/particles and spawn the rats.
    I am using popup-box function to add some Area Descriptions (I like that you can use them to pause the game as well).

    Questions:
    1# Are the rats (smaller ones, not the DireRats) supposed to do always zero damage?
    Or is it a bug created on my side?
    From the manual, even if they have 1d3-4, they should always do a minum of 1 damage.
    https://www.d20srd.org/srd/monsters/rat.htm

    Minimum Damage
    If penalties reduce the damage result to less than 1, a hit still deals 1 point of damage.
    When compare with the rat familiar:
    upload_2023-1-9_15-59-13.png
    Is the +4 damage added by the dev on the proto to counter the -4 from STR?
    Rat familiar also have disease as the DireRat.

    Is the rat familiar the only small rat found in the game ?
    Co8 Rats List:
    {12050}{Rat} #obj_t_generic
    {14056}{Dire Rat} #small#1008#3510#1d4#disease
    {14068}{Vapor Rat} #small#1009#3550#1d4-4 | mc_type_magical_beast
    {14405}{Fiendish Dire Rat} #same as DireRat but extraplanar, monster resistances
    {14433}{Giant Rat} #no disease
    {14451}{Legendary Rat} #small#1008#3510#1d8+2#disease --> Is this for WildShape only?
    {14905}{Rat} #This is the rat familiar | tiny#1008#8200#1d3#disease
    {14998}{Dire Rat} #same as DireRat

    I need to choose whether to keep them like this (possibly adding the Flanker AI strategy to help the dire rats?) or to add the damage or disease to them.
    What do you think about it?
     
    Illustair, August, anatoliy and 2 others like this.
  2. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    I've added new portraits for Rats and Dire Rats.
    upload_2023-1-11_14-11-47.png
    upload_2023-1-11_14-12-6.png
    I'll probably change them again in the future, but for now, I just wanted to have something visually different between the two.
    I was thinking about preparing a training set for AI like stable diffusion for future portraits, but for the moment the NPCs present in ADASK are few and it's not worth it.

    I have reworked the proto for the basic Rat searching the forum for information about how to properly set damage, hitbonus, and skill points for monsters.
    I have found these threads:
    Creature feature: https://co8.org/community/threads/creature-feature.4370/#post-45089
    Meaning of various object flags: https://co8.org/community/threads/meaning-of-various-object-flags.3088/
    Protos.tab fields: https://co8.org/community/threads/protos-tab-fields.532/#post-3022

    My rats do no damage with what appear to be the correct settings in the prototype.
    And that is what it should do as the Manual entry; I now have to find a way to add the minimum damage of 1 to them

    I discovered this section in Spell760 - Summon Familiar.py that explains why the familiar rat has a different profile than the standard rat:
    upload_2023-1-11_17-1-29.png
    The Rat Familiar has a value of 6 in the "To Hit Bonus" of the proto.
    The game Attack window shows a +8 BaseAttack (+2 from?)
    For a Wizard of first level (BAB +0), the "To Hit Bonus" from his master would be zero, so I don't think it counts in my test.
    Then it has -4 STR and +2 DEX correctly listed.
    I'm still not sure where the +4 damage comes from, given that the creature damage dice is 1d3.

    I have also noticed that the Rat familiar does one kind of damage the first time it attacks (unarmed attack), and from the second turn on, uses the Bite attack as saved in the Proto.
    Tested vs. the poor village boy at the start of Hommlet (Co8 NC only and with Temple+):

    upload_2023-1-11_18-14-44.png

    I think I'll try Temple+ modifiers to add the minimum damage of 1 to my Rats.
    I hope to learn new stuff in the process.
     
    Illustair, anatoliy and Buffed Rabbit like this.
  3. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    Popup box
    The popup-box at the entrance will probably be removed at the end, but I am still thinking about using them in other situations.
    There are now two popup boxes that spawn in two different situations, based on party location.
    - If the party is in the main hall when the doors break down, it will show this:
    2023-01-14_103351.png
    - If the party is inside the room on the right when it happens, it will show this instead:
    2023-01-14_103447.png

    Damage
    I didn't find a way to ignore the zero damage and add a rule for the minimum 1 damage so I have modified the Rat proto "Creature Damage Dice" to 1d1.
    This is what it shows inside the game:
    2023-01-14_104651.png

    I'll keep it for now.

    AI
    I have added a check for the Rats inside san_start_combat so that if the two Dire Rats are dead, the other small rats run away from the fight.
    I had some difficulties with the script, in the end I have decided to use a obj.run_to() plus a timed destroy (return SKIP_DEFAULT).

    I need to find a way to add the rats that ran away to the experience calculation for the encounter.
     
  4. anatoliy

    anatoliy Established Member

    Joined:
    Feb 18, 2017
    Messages:
    635
    Likes Received:
    201
    Code:
                
    last = attachee.obj_get_obj(toee.obj_f_npc_who_hit_me_last)
    if not last: last = game.party[0]
    attachee.critter_kill_by_effect(last)
    You can do this right before .destroy() in post destroy call.
     
    Mellonril likes this.
  5. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    Hi, @anatoliy !
    Thanks for the suggestion.
    Would it be correct to add the experience even if the rats ran away? I have to think about it, but I'll sure try the code above when I have a moment.

    I loved your work; I learned a lot of Python basics (I am not a coder) going through your mods!
    Thanks for creating them.
     
    anatoliy likes this.
  6. anatoliy

    anatoliy Established Member

    Joined:
    Feb 18, 2017
    Messages:
    635
    Likes Received:
    201
    By D&D rules - yes.

    But for players I as Dungeon Master never do that. Because it's rather disappointing for them, especially when "fat and tasty" dude with treasure does that.

    I'd rather back away him to some corner, imitating his flee behavior to make a Player feel happy about that, but then make the rat attack again.
     
  7. FDR4PREZ

    FDR4PREZ Established Member

    Joined:
    Apr 10, 2007
    Messages:
    534
    Likes Received:
    80
    I think technically the answer is yes because they were "defeated" in that encounter - even though they did not die because the fled.

    It can be abused by the players if they start a battle and try to get the enemy to retreat without much combat. I think this is where the CR stuff was introduced and not getting much, if any, experience if you are overpowering over the enemy.

    They aren't a mindless zombie that ran away due to being turned, just to attack again when the effects wear off...

    I guess for a 'real' animal, they can run off completely and never be seen again. I'd venture to guess that if the animal runs away and then is cornered then it will not have choice but to reengage and attack again, but some would run away to fully disengage combat (fight or flight).

    They can run away, but if they are still in the same area on the map them maybe they re-engage if the player approaches again.
     
    Mellonril likes this.
  8. FDR4PREZ

    FDR4PREZ Established Member

    Joined:
    Apr 10, 2007
    Messages:
    534
    Likes Received:
    80
    it's like the same concept that if you disengage combat you don't get any experience for that encounter.

    So if you kill 3 of the 10 enemies and then you flee, then you get 0-xp for the encounter.
     
  9. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    I am working on the chest room now.
    upload_2023-1-16_21-26-42.png
    I have already completed the background graphics and tile blocking; the remaining editor setup was merely adding the lighting and particles, and the most boring part: the clipping geometry.

    upload_2023-1-16_21-28-35.png
    The chest model is one of the standard chests with a new texture (it should be made of stone).
    Is there a limit where I should keep the texture resolution for the 3D model within the original size, or may I go up to 256 pixels?

    The proto for the chest has a trap connected (id 19) with a script for san_trap, and I have modified the trap.tab adding TRAP_FUSILLADE_OF_DARTS.
    I began with Anatoliy's work and excellent documentation (https://co8.org/community/threads/trap-specification.13045/) and tried to add and change a few things.

    Trap description from the module:
    Trap: The lid of the chest is unlocked, but it is connected to a trap that fires a fusillade of darts at everyone in the same 5-foot square as the chest and in every square adjacent to it or sharing a corner with it. A character must be in the same square as the chest to open it unless she declares that she’s opening it with a weapon or pole. Attacking the chest also sets off the trap.
    Fusillade of Darts Trap: CR 1; mechanical, touch trigger (attached); manual reset; Atk +10 ranged (1d4+1, dart); multiple targets (fires one dart at every target in a 5-ft. square adjacent to the chest); Search DC 20; Disable Device DC 20;

    Search DC 20 and DD DC 20 were added to the trap entry inside traps.tab.
    The damage, range, and targets are set in the trap script.

    Code:
    import toee
    
    # TRAP_FUSILLADE_OF_DARTS from traps.tab (id 19) Search DC 20, Disable DC 20, CR 1
    
    def san_trap(trap, triggerer):
        assert isinstance(triggerer, toee.PyObjHandle)
        assert isinstance(trap, toee.PyTrap)
        trap_trigger_darts_fusillade(trap.obj, triggerer, trap.partsys, trap)
        return toee.SKIP_DEFAULT
    
    def trap_trigger_darts_fusillade(attachee, triggerer, partsys, trap):
        assert isinstance(triggerer, toee.PyObjHandle)
        assert isinstance(trap, toee.PyTrap)
    
        toee.game.particles(partsys, attachee) #'Trap-Adask-DartsFusillade'
        toee.game.sound(4023, 1)
        attachee.float_text_line('Trap triggered!')
    
        dice = toee.dice_new("1d4+1") # base damage
        dice_crit = toee.dice_new("2d4+2") # crit damage
        radius_ft = 7 # trap attack range
    
        for obj in toee.game.obj_list_range(attachee.location, radius_ft, toee.OLC_CRITTERS):
            f = obj.object_flags_get()
            if ((f & toee.OF_OFF) or (f & toee.OF_DESTROYED) or (f & toee.OF_DONTDRAW)): continue
            caf = trap.attack(obj, 10, 20, 1) # Atk +10
            if (not (caf & toee.D20CAF_HIT)):
                obj.float_mesfile_line('mes\\combat.mes', 29) # Miss
                continue
            dce = dice
            if (caf & toee.D20CAF_CRITICAL):
                dce = dice_crit
                obj.float_mesfile_line('mes\\combat.mes', 12) # Critical Hit!
            obj.damage(trap.obj, toee.D20DT_PIERCING, dce, toee.D20DAP_NORMAL, toee.D20A_UNSPECIFIED_ATTACK)
            toee.game.particles('hit-UNSPECIFIED-light', obj)
            obj.anim_goal_push_hit_by_weapon(attachee) # Do animation hit by weapon
        return
    I have added float lines in case of a missed or critical attack, sound (temp), particles of blood when hit, and a test with anim_goal for the animation.
    The trap particles from the default dart trap in the game were not working for me when spawned by script, so I've created new ones.
    The range of the particles should visually match the range set inside the script.
    upload_2023-1-16_22-4-48.png upload_2023-1-16_22-5-17.png

    upload_2023-1-16_22-11-40.png



    There are probably still a lot of bugs. Is the situation depicted in the video normal?
    • The thief makes a concentrated search, finds the trap, and tries to disable it.
    • He fails to deactivate the trap, and it is set off.
    • Then another PC tries to open the chest, and the trap is triggered again.
    • The third PC then opens the chest without further triggering the trap.
    If I test the same situation without disabling the trap but only opening instead, it will trigger just the first time and then be disabled and safe to open.

    Finally, what do you think about the trap's difficulty?
    Isn't a DC of 20 a bit too high to disable the trap for a party of level 1?
    Should the trap attack give a reflex save to the party?
    Also, without a thief, there is no way to open the chest without triggering the trap.
    However, the trap contains a potion of cure light wounds, so it may be self-balancing.
     
    Illustair, Buffed Rabbit and _doug_ like this.
  10. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    It seems that the command obj.run_to makes the npc run to a set position and, when completed, return to the initial location.
    Is there a different command that makes the monsters move to a location and stop there?

    example code
    Code:
        obj = toee.game.obj_create(PROTO_NPC_ANIMAL_DIRE_RAT, utils_obj.sec2loc(501, 449)) # spawn rat
        obj.move(utils_obj.sec2loc(501, 449)) # force rat to location
        obj.rotation = const_toee.rotation_0400_oclock # rotate rat
        obj.run_to(utils_obj.sec2loc(513, 463)) # run to location
     
     
    Buffed Rabbit and rojay like this.
  11. marc1967

    marc1967 Established Member

    Joined:
    Jan 19, 2014
    Messages:
    708
    Likes Received:
    112
    That looks like a lot of Temple+ functions, which is way beyond my pay-grade.

    I can only tell you is how I have always got an NPC to run to a new location.

    I reset his standpoints to the spot you want the NPC to run to, using the funtion obj.standpoint_set(). This requires creating a jumppoint in data\rules\jumppoint.tab. So, if your map is 5064, and you want him to run to location 513, 463, just pick an unused jumppoint number (525 in my example below) and set a line in jumppoint.tab like this (note, those must be TABs between each of the five entries in that line):
    Code:
    525   Rat Location after running   5064   513   463
    Then these commands will reset the standpoints, and he immediately starts running to his new location automatically:
    Code:
    obj.standpoint_set( STANDPOINT_DAY, 525)
    obj.standpoint_set( STANDPOINT_NIGHT, 525)
    This is obviously not very convenient if you want to set many locations, since each one will need a new jumppoint.
     
    Mellonril and Buffed Rabbit like this.
  12. anatoliy

    anatoliy Established Member

    Joined:
    Feb 18, 2017
    Messages:
    635
    Likes Received:
    201
    The game has non-combat and combat modes which require different approach for Python programming. It's different to IWD (Infinity) games in that regard.

    When in non-combat there is timed repeated code (NPC Wander function), which will direct NPCs to do certain things:
    • Move to standpoint
      • Move to night standpoint
      • Move to day standpoint
    • Follow waypoint steps
    So if you want to make NPC move to specific coordinates and STAND there you should make sure that NPC Wander will not move him back. To do that you should set standpoint for NPC:
    Code:
    def npc_goto(npc, x, y):
        assert isinstance(npc, toee.PyObjHandle)
        assert isinstance(x, int)
        assert isinstance(y, int)
        loc = utils_obj.sec2loc(x, y)
        return npc_goto_loc(npc, loc)
    
    def npc_goto_loc_full(npc, loc, off_x, off_y):
        assert isinstance(npc, toee.PyObjHandle)
        assert isinstance(loc, int)
        x, y = utils_obj.loc2sec(loc)
        print("goto {}, {} ({}, {}) of {}".format(x, y, off_x, off_y, npc))
        npc.standpoint_set(toee.STANDPOINT_DAY, -1, loc, 0, off_x, off_y)
        npc.standpoint_
    @marc1967 is correct, but he uses legacy approach to define standpoint list in file and then reference it in the code. But we have improved that by allowing direct location in standpoint_set.

    You can use run_to in conjunction with standpoint_set.

    As for combat mode it's very very different, essentially you must use npc.ai_strategy_set_custom for that.
     
    Mellonril and Buffed Rabbit like this.
  13. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    Thanks @anatoliy
    I had found some notes on the Temple+ Scripting API that suggested the problem above:
    and tried to add a standpoint for one rat.

    It worked, but as @marc1967 mentioned, I had to create one standpoint for each rat (and thus n new jump joints).
    (Is there a limit on the total number of entries in the jumppoint.tab?)

    I was not aware of the new changes on Temple+, but now that I can set location directly for the standpoints, it opens up new possibilities.
    I've tested the new code changes, and now that the rats have their standpoints, they stay still after they reach the run_to command target location. Great!

    The rats' movements will probably not even be noticed by the player (maybe if he is sneaking or invisible when the rats break into the room), as they should attack and start combat the first time they see the party PCs.
    But I am experimenting to learn which are the limits of modding, so thanks to everyone for their time and suggestions!
     
  14. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,627
    Likes Received:
    538
  15. Mellonril

    Mellonril Member

    Joined:
    Apr 6, 2008
    Messages:
    13
    Likes Received:
    22
    wip screenshots map progress
    upload_2023-1-28_11-58-58.png
    I am still battling with clipping issues...
    We still don't have any working plugins to export DAG geometry from Blender or another 3D software, or am I wrong?
    It would be amazing to be able to reuse my existing level geometry as clipping data.

    upload_2023-1-28_12-4-18.png
     

    Attached Files:

    August and Buffed Rabbit like this.
Our Host!