Need an Opinion, Monster Useless When Stood On

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
I've been working on a monster mostly in the last couple of days, however, I found a fatal flaw with it. You can probably guess by the title, but before I explain, I should note that the gimmick of this monster is that they're most effective in groups. While one can hardly do anything, 5+ can be deadly when all are attacking one thing. When you stand on them however, only one is able to attack you at a time, due to the range of the attacks and the enemies bboxes blocking each other. Sometimes one can't even manage to attack you. Increasing the range of the attacks doesn't help at all. They have a tendency to stand on each other, but the problem persists even when changing the bboxes so they can't stand on each other. The scripts in case you need 'em, only these two are required for it to run. Sounds will probably only work in the alpha build because of $get_random_token(). You will need this model in order for this script to work:
https://www.dropbox.com/s/lgc4q7crntkwhl0/chumtoad.mdl?dl=1
Put in msc/models/monsters.

Chumtoad:
Code:
//Chumtoad by greatguys1
//This doens't follow the original concept of it from hl1 what-so-ever.
//The idea is to have it like "zerg" where they're most effective in numbers, having damage scale higher the more it attacks, and even more when more are attacking the same target.

#scope server

{
    //Settings for base monster new
    setvar ANIM_RUN "hop_1"
    setvar ANIM_WALK "hop_1"
    setvar ANIM_IDLE "idle"
    setvard ANIM_ATTACK "flinch2"

    setvard NPC_NO_ATTACK 0
   
    setvard ATTACK_MOVERANGE 10
    setvard ATTACK_RANGE 50
    setvard ATTACK_HITRANGE 60
    const ATTACK_HITCHANCE 80

    const FLEE_HEALTH 0
    const FLEE_CHANCE 0

    setvard CANT_FLEE 1
    setvard CAN_RETALIATE 1
    setvard CAN_FLINCH 0

    //damage_stack_mult
    const STACK_DAMAGE_SCRIPT "test_scripts/damage_stack_mult"
    const STACK_PARAM2 5
    const STACK_PARAM3 0.1
    const STACK_PARAM4 10

    //Custom Blink cycle //If anyone wants to add a blink cycle to the other skins, I'll gladly accomondate it here
    const BLINK_FREQ 5
    const BLINK_DUR 0.2 //For each step
    setvard BLINK_STEP 0
    const BLINK_MAX 2 //Two other than the idle, eye open state.
    setvard BLINK_CYCLE 1 //1 for up, -1 for down

    //Suicide chances
    const SUICIDE_CHANCE 100 //% chance
    const SUICIDE_THRESHOLD 0.7 //Multiplier of what health I need to be under before I check against chance to explode
    const SUICIDE_DISTANCE 100 //Distance affected by explosion
    setvard SUICIDING 0
    setvard TRIED_SUICIDE 0

    //Other stuff particular to this script
    const ANIM_CHARGE "idle2"
    const ANIM_SUICIDE "flinch1"
    setvard ANIM_IDLE3_CHANCE 5 //% chance to use the idle3 animation, slower but more damage
    const LONG_ATTACK $rand(50,60) //Not long range, but taking longer to strike.
    const SHORT_ATTACK $rand(10,20)

    const MONSTER_HEALTH $get(ent_me,maxhp) //I think this'll grab it again should it change.

    const GIB_BURSTER_SCRIPT "effects/sfx_gib_burst_alien"
    const GIB_BURSTER_FORCE 300

    const EXPLOSION_SFX "effects/sfx_sprite"
    const EXPLOSION_SPRITE "oculus/exp_green.spr"
    const EXPLOSION_SFX_SOUND "monsters/tube/Tube_ExplodingDeath.wav"

    const DEATH_SOUNDS "monsters/bat/pain1.wav;monsters/bat/pain2.wav;monsters/beetle/idle.wav2;monsters/beetle/idle3.wav"
    const IDLE_SOUNDS "monsters/ogre_welp/bc_idle2.wav;monsters/ogre_welp/bc_idle3.wav;monsters/ogre_welp/bc_idle4.wav"

    const PAIN_SOUNDS "monsters/ogre_welp/bc_pain1.wav;monsters/ogre_welp/bc_pain2.wav;monsters/ogre_welp/bc_pain3.wav"
    const PAIN_SOUND_RATE 100 //percent chance to play pain sound

    const HIT_SOUNDS "monsters/tube/TubeCritter_Hit1.wav;monsters/tube/TuberCritter_Hit2.wav;monsters/tube/TubeCritter_Hit3.wav"
    const HIT_SOUND_RATE 100 //percent chance to play hit sound
}

#include monsters/base_monster_new
#include test_scripts/monster_explode

{ game_precache

    precache monsters/chumtoad.mdl
    precache EXPLOSION_SPRITE
    precache agibs.mdl
}

{ npc_spawn

    name "a|Chumtoad"
    race vermin
    hp 300
    setmodel "monsters/chumtoad.mdl"
    width 20
    height 20
    setmoveanim ANIM_RUN
    setidleanim ANIM_IDLE
    blood green
    roam 0

    //setvard MONSTER_HEALTH $get(ent_me,hp)
    setvard SOUND_CHANNEL $rand(0,100) //I dunno what the max is

    callevent BLINK_FREQ do_blinking
}

//{ cycle_down

//    playanim loop ANIM_IDLE
//}

{ do_blinking

    local L_PROPS $get(ent_me,renderprops)
    local L_SKIN $get_token(L_PROPS,4)

    setvard BLINK_STEP $math(add,BLINK_STEP,BLINK_CYCLE)

    if ( BLINK_STEP >= BLINK_MAX )
    {
        setvard BLINK_CYCLE -1
    }
    else if ( BLINK_STEP <= 0 )
    {
        setvard BLINK_CYCLE 1
    }

    if ( L_SKIN < 3 )
    {
        setprop ent_me skin BLINK_STEP
    }

    if ( BLINK_STEP == 0 )
    {
        callevent BLINK_FREQ do_blinking
    }
    else
    {
        callevent BLINK_DUR do_blinking
    }
}

{ game_damaged

    local L_HP $get(ent_me,hp)

    if ( L_HP <= $math(multiply,MONSTER_HEALTH,SUICIDE_THRESHOLD) )
    {
        if ( !TRIED_SUICIDE )
        {
            setvard TRIED_SUICIDE 1

            if ( $func(func_test_chance,SUICIDE_CHANCE) )
            {
                callevent do_suicide_charge
            }
        }
    }

    if ( $func(func_test_chance,PAIN_SOUND_RATE) )
    {
        playsound SOUND_CHANNEL 10 $get_random_token(PAIN_SOUNDS)
    }

    return **clear
    returndata 1 //A bug exists that a $func within one of these functions will overwrite the returndata. Resetting it.
}

{ game_damaged_other //PARAM1=target_hit PARAM2=dmg PARAM3=dmg_type //items return <attack_callback>_damaged_other

    local L_TARGET PARAM1
    applyeffect L_TARGET STACK_DAMAGE_SCRIPT $get(ent_me,name) STACK_PARAM2 STACK_PARAM3 STACK_PARAM4

    if ( $func(func_test_chance,HIT_SOUND_RATE) )
    {
        playsound SOUND_CHANNEL 10 $get_random_token(HIT_SOUNDS)
    }

    return **clear
    returndata 1 //A bug exists that a $func within one of these functions will overwrite the returndata. Resetting it.
}

{ game_death

    playsound SOUND_CHANNEL 10 $get_random_token(DEATH_SOUNDS)
}

{ do_suicide_charge

    callevent npcatk_suspend_ai
    playanim critical ANIM_CHARGE
    setvard SUICIDING 1
}

{ finish_charge //Called from the model

    playanim critical ANIM_SUICIDE
}

{ finish_suicide //Called from the model

    setprop ent_me rendermode 5
    setprop ent_me renderamt 0

    callevent do_explode
    callevent npc_suicide
}

{ do_explode

    local L_VEC $get(ent_me,origin)
    vectoradd L_VEC $vec(0,0,30)

    local L_COL $clcol(0,0,0)
    local L_STR "0;3;255;add;"
    stradd L_STR L_COL
    stradd L_STR ";20;10"

    clientevent new all EXPLOSION_SFX L_VEC EXPLOSION_SPRITE L_STR 0.5
    clientevent new all GIB_BURSTER_SCRIPT L_VEC GIB_BURSTER_FORCE
    playsound 0 10 EXPLOSION_SFX_SOUND
}

{ npc_selectattack

    if ( $rand(0,100) <= ANIM_IDLE3_CHANCE )
    {
        setvard ANIM_ATTACK idle3
    }
    else
    {
        setvard ANIM_ATTACK flinch2
    }
}

{ attack_1 //Called from the model

    callevent npcatk_dodamage NPCATK_TARGET ATTACK_HITRANGE LONG_ATTACK ATTACK_HITCHANCE pierce
}

{ attack_2 //Called from the model

    callevent npcatk_dodamage NPCATK_TARGET ATTACK_HITRANGE SHORT_ATTACK ATTACK_HITCHANCE pierce
}

{ func_test_chance //Returns 1 on a win, 0 on fail. PARAM1 = % to win

    local L_CHANCE PARAM1
    local L_ROLL $rand(1,100)

    if ( L_ROLL <= L_CHANCE )
    {
        return 1
    }
    else
    {
        return 0
    }
}

Attack Multiplier Stacker:
Code:
//Increasing damage from the same monster / same type of monster via a multiplier. Originally built for the chumtoad. By greatguys1
//If you can think of a better name that best describes what this does, please change it, as the current name is inaccurate/confusing.
//I'm not using the effect_base as it doesn't seem to have anything that would pertain to this.

//Accepts params:
//1; Creature name
//2; Expire time
//3; multiplier increment factor
//4; max multiplier

#scope server

{
    const CHECK_STACK_TIME 1 //Will always check at one second increments

    setvard CUR_MULT 1
    setvard ACTIVATED 0
    setvard DAMAGED_THIS_TICK 0
    setvard TICKS_NOT_DAMAGED 0
}

{ game_precache

    local L_ID "stack_damage"

    setvard reg.effect.name L_ID
    setvard reg.effect.id L_ID
    setvard reg.effect.flags "nostack"
    setvard reg.effect.script $currentscript
    setvard game.effect.removeondeath 1
    setvard game.effect.id L_ID
    setvard game.effect.displayname "Zerg Swarm Debuff"
    setvard game.effect.flags "nostack"
    setvard game.effect.type "nostack"

    registereffect
}

{ game_activate //PARAM1 = creature name, not full PARAM2 = expire time

    setvard CREATURE_STACK_NAME PARAM1
    setvard EXPIRE_TIME PARAM2
    setvard INCREMENT_FACTOR PARAM3
    setvard MAX_MULT PARAM4

    setvard ACTIVATED 1
    callevent check_stack
}

{ check_stack

    if ( ACTIVATED )
    {
        if ( !DAMAGED_THIS_FRAME )
        {
            setvard TICKS_NOT_DAMAGED $math(add,TICKS_NOT_DAMAGED,1)
            if ( TICKS_NOT_DAMAGED == EXPIRE_TIME )
            {
                callevent effect_die
            }
        }
        else
        {
            setvard TICKS_NOT_DAMAGED 0
            setvard DAMAGED_THIS_FRAME 0
        }
    }

    callevent CHECK_STACK_TIME check_stack
}

{ game_damaged //PARAM1=attacker PARAM2=dmg PARAM3=dmg_type PARAM4=accuracy_roll PARAM5=skill PARAM6=Inflicter

    if ( ACTIVATED )
    {
        local L_ATTACKER PARAM1

        if ( CREATURE_STACK_NAME contains $get(L_ATTACKER,name) )
        {
            local L_DAMAGE PARAM2
            setvard CUR_MULT $math(add,CUR_MULT,INCREMENT_FACTOR)
           
            if ( CUR_MULT > MAX_MULT ) //I can't find the clamp command
            {
                setvard CUR_MULT MAX_MULT
            }

            local L_DAMAGE $math(multiply,L_DAMAGE,CUR_MULT)

            returndata CUR_MULT
            //setdmg dmg L_DAMAGE
            setvard DAMAGED_THIS_FRAME 1
        }
    }
}

{ game_death

    callevent effect_die
}

{ effect_die

    removescript
}

//{ game_duplicated
//
//    callevent effect_die
//}

How should I fix this? Is there a way to make attacks go through entities of the same name/script? Should I make them flee for 1 second if the player is significantly higher enough than the chumtoad? Should I add an ability to throw the player off the chumtoad? Or perhaps a different solution?
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Problem here is that their attacks are hit-scan attacks - so they are blocked by their buddies. You can switch em up for AOE attacks - that may fix it, though it's rougher on the resources. If it turns out to be too rough, or doesn't fix it, you'll have to resort to direct damage attacks. You can do a world only traceline to their target to ensure they at least won't attack through walls.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
I assume the idea here is the more often you get bit the stronger the damage gets...

Might be worth noting this is somewhat similar to how the spider webbing works... In this case, however, there's no visual effect to go with, so it could be done much more efficiently by simply adding a tracker to player/externals (and monster/externals as well, so it has the same effect on summons and other enemy NPCs.) Or... Better yet, use the scriptflag system.

eg.
Code:
{ chumbite_damaged_other //assumes dmgevent:chumbite on your chumtoad's xdodamage attack.
    if PARAM1 //I hit
    if $get(PARAM2,relationship,ent_me) equals enemy //didn't hit one of my friends
    local L_CUR_MULTI $get_scriptflag(PARAM2,stackchum,name_value) //find the total value of stackchum
    if ( L_CUR_MULTI < 1 ) local L_CUR_MULTI 1 //in case it's our first bite
    setdmg dmg $math(multiply,DMG_STACK,L_CUR_MULTI) //DMG_STACK being the critter's base damage
    returndata L_CUR_MULTI //welcome to the wonderful world of MSC redundancy

    //scriptflags <target> add <name> <type> [value:1] [expiretime:-1] [expiremsg:none]
    scriptflags PARAM2 add stackchum dmgstack 1 10.0 none //add 1 stack that decays in 10 seconds
}
And there ya have it all in one.

Mind, you really need to move this to a dmgevent, otherwise if the mapper chooses to add DOT to these buggers via addparams, those stacks are gonna go up with every DOT tick, as will the DOT damage.

Also mind with critters that explode you lose a lotta XP. Something for the mapper to keep in mind - they may need to add extra treasure or some easy XP to elsewhere to compensate. Might be kind to give them a set_no_explode as an addparam option.

edit:
Also...
Code:
    if ( $func(func_test_chance,HIT_SOUND_RATE) )
    {
        playsound SOUND_CHANNEL 10 $get_random_token(HIT_SOUNDS)
    }
Not... Quite sure what yer trying to do here. I mean HIT_SOUND_RATE is a constant 100, so...

Okay, first off SOUND_CHANNEL is not 1-100 as you have it defined in the script - you'll probably find that the attack sound often does not play, or sounds odd. The legit sound channels are as follows:
Code:
    setvarg CHAN_AUTO    0 //first free channel (emanates from entity)
    setvarg CHAN_WEAPON    1 //player HUD channel
    setvarg CHAN_VOICE    2 //entity "voice" channel (emanates from entity)
    setvarg CHAN_ITEM        3 //player back HUD channel (client only)
    setvarg CHAN_BODY    4 //entity "body" channel (emanates from entity)
    setvarg CHAN_WATHER    5 //global weather channel

Unless it's a looping sound you need to cancel, or one that needs to "follow" the entity around (and in either case it should be a server side sound via svplaysound), or you are playing two overlapping sounds in the same frame, usually you can just stick with 0. Thus:
Code:
playsound 0 10 $get_random_token(HIT_SOUNDS)
It might also be worth noting this is what "playrandomsound" is for. In which case it'd look like this:
Code:
const SOUND_ATTACK1 monsters/tube/TubeCritter_Hit1.wav
const SOUND_ATTACK2 monsters/tube/TuberCritter_Hit2.wav
const SOUND_ATTACK3 monsters/tube/TubeCritter_Hit3.wav
[...]
playrandomsound 0 10 SOUND_ATTACK1 SOUND_ATTACK2 SOUND_ATTACK3
Though I suppose it probably doesn't save much processing time - token system works fine.

It would save some processing time to simply check the random generated number rather than calling another event via $func(), since calling events does add a smidgen of overhead (plus script size). So:
Code:
if ( $rand(1,100) < SOME_CHANCE )
Albeit, that's for the others - in this case you're checking against 100, so might as well axe it entirely.

If you want to get sophisticated, you could scale the pitch and/or volume as the hits become stronger. Both have to be integers, I believe. Volume is 0-10, and pitch 1-400 (with 100 being default - anything over 200 tends to get hypersonic and below 25 unintelligible). Pitch only works with 8-bit sounds though - not sure if those are 8 or 16 bit waves.

Using the above stack system, you could:
Code:
{
[...]
    const MAX_HIT_VOL 10
    const MAX_HIT_PITCH 200
    const MAX_DMG_STACKS 10
[...]
}
[...]
{ chumbite_damaged_other //assumes dmgevent:chumbite on your chumtoad's xdodamage attack.
    if PARAM1 //I hit
    if $get(PARAM2,relationship,ent_me) equals enemy //didn't hit one of my friends
    local L_CUR_MULTI $get_scriptflag(PARAM2,stackchum,name_value) //find the total value of stackchum
    if ( L_CUR_MULTI < 1 ) local L_CUR_MULTI 1 //in case it's our first bite
    setdmg dmg $math(multiply,DMG_STACK,L_CUR_MULTI) //DMG_STACK being the critter's base damage
    returndata L_CUR_MULTI //welcome to the wonderful world of MSC redundancy

    //adjust attack sound by stacks
    local L_VOL_RATIO $int($ratio($math(divide,L_CUR_MULTI,MAX_DMG_STACKS),1,MAX_HIT_VOL))
    local L_PITCH_RATIO $int($ratio($math(divide,L_CUR_MULTI,MAX_DMG_STACKS),50,MAX_HIT_PITCH))
    capvar L_VOL_RATIO 1 10 //probably redundant, just safety
    capvar L_PITCH_RATIO 50 200 //probably redundant, just safety
    playsound 0 L_VOL_RATIO $get_random_token(HIT_SOUNDS) 0.8 L_PITCH_RATIO

    if L_CUR_MULTI < MAX_DMG_STACKS //don't add another stack if target at max (as defined by MAX_DMG_STACKS)
    //scriptflags <target> add <name> <type> [value:1] [expiretime:-1] [expiremsg:none]
    scriptflags PARAM2 add stackchum dmgstack 1 10.0 none //add 1 stack that decays in 10 seconds
}
Which would cause the volume and (should the wave file allow) pitch to increase as the hits strengthen. (Granted, those strongest hits will probably do as much damage to the player himself as to their character, as they'd likely be being ear shattering squeals.)
 
Last edited:

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
Problem here is that their attacks are hit-scan attacks - so they are blocked by their buddies. You can switch em up for AOE attacks - that may fix it, though it's rougher on the resources. If it turns out to be too rough, or doesn't fix it, you'll have to resort to direct damage attacks. You can do a world only traceline to their target to ensure they at least won't attack through walls.
I'll probably try the direct damage with a world traceline.

Might be worth noting this is somewhat similar to how the spider webbing works... In this case, however, there's no visual effect to go with, so it could be done much more efficiently by simply adding a tracker to player/externals (and monster/externals as well, so it has the same effect on summons and other enemy NPCs.) Or... Better yet, use the scriptflag system.
I'm not too into adding stuff like this to the externals- 99% of the time it'll never be used. What would be the advantage to having it there, rather than an effect? I will experiment with the scriptflags though. Ideally, what would you use scriptflags for?

Not... Quite sure what yer trying to do here. I mean HIT_SOUND_RATE is a constant 100, so...
Yeah- I wanted to make it random when it'd play a noise but it feels better to have the sounds play every time. Haven't got to removing it yet. As for...

Okay, first off SOUND_CHANNEL is not 1-100 as you have it defined in the script - you'll probably find that the attack sound often does not play, or sounds odd. The legit sound channels are as follows:
Haven't had any issues yet. But I will change it. I assumed the sound channel was a system in which only a certain amount of sounds could be played at a time from index 0 to x, similar to how old consoles have only so many "voices". I wanted the chumtoad to only be able to emit one sound at a time, overwriting the current sound being played if a new one was played.
What exactly is a sound channel in this instance?

It might also be worth noting this is what "playrandomsound" is for. In which case it'd look like this:
Probably would look neater like that. Will change.

It would save some processing time to simply check the random generated number rather than calling another event via $func(), since calling events does add a smidgen of overhead (plus script size). So:
Normally it'd feel neater to have a function, but seeing as how I'm removing all but one of those instances, I'll end up doing this.

Which would cause the volume and (should the wave file allow) pitch to increase as the hits strengthen. (Granted, those strongest hits will probably do as much damage to the player himself as to their character, as they'd likely be being ear shattering squeals.)
This is a neat idea. Gives indication that something is actually happening with the damage stacking, rather than just larger numbers. The Snark sounds would be perfect for this.

Thanks for looking it over, I really appreciate the feedback.
Also thanks for letting me see the forum again

Missed a few things:
Mind, you really need to move this to a dmgevent, otherwise if the mapper chooses to add DOT to these buggers via addparams, those stacks are gonna go up with every DOT tick, as will the DOT damage.
Will do

Also mind with critters that explode you lose a lotta XP. Something for the mapper to keep in mind - they may need to add extra treasure or some easy XP to elsewhere to compensate. Might be kind to give them a set_no_explode as an addparam option.
Will make that as a param, probably just for straight explode chance.
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
I'm not too into adding stuff like this to the externals- 99% of the time it'll never be used. What would be the advantage to having it there, rather than an effect? I will experiment with the scriptflags though. Ideally, what would you use scriptflags for?
Applyeffects add a lot of overhead, particularly if they get applied often. You're loading up a new script in real time and all the work that goes with sticking it onto the entity. It creates a temporary entity to do this, so you're effectively spawning a monster (albeit, a light one).

I guess you weren't here way back in the day when the heal spell was a stacking applyeffect that healed for about 0.1 per second, per stack, each of which lasted for about 20 seconds. Basically, anyone spamming a heal spell would send the server into paralysis for good long time (was kinda the troll thing to do for awhile).

It's also among the reasons I wanna move all the DOT effects to an #include of externals using the scriptflags system. The fact that DOT are applyeffects is the primary reason they lag.

So, yeah, if the scriptflags are too complex, adding an external counter and pulling the resulting scriptvar requires all of six lines or so in the externals.

Reminds me, I also need to make a shared externals, as there's a lot of externals like that which apply to both player and monsters.

(Also, sorry about the whole fuxing the forums for ya thing - WotN's subforum is being a pain - still learning the new forum ops.)

What exactly is a sound channel in this instance?
Each entity in Half-life has five (actually six) reserved sound channels you can designate, as described in that post, and then yeah, another 55 dynamic ones it allocates automatically. Ya use 0 for automatic, and then one of those other channels when you need to be able to alter the sound later, or, in MSC's case, have it follow an entity around, and the like.

With svplaysound you can put an autoloop sound on an entity, and then change its pitch, volume, and/or attenuation later by playing the same sound on the same channel. You cancel the loop by playing that same sound on the same channel at 0 volume. If you have two different sounds you want to start at the same time in the same event, ya also need to set channels for them (though that can be done with regular old playsound, and doesn't need to be server-side). Server-side sounds played on channels 1, 2, or 4 will follow the entity around. ...and I *think* playing the same sound on the same channel with "svsound.play3d" will cause it to relocate, rather than make a new instance of the same sound (no promises, can't recall if I tested that). The disadvantage to server-side sounds is that they need to be precached - or game go smoosh - and this adds to critical overhead. Check your log_msdll.log for sounds that are already precached universally (all the sounds listed before "World Spawn END"). Ya can re-use any of those without adding overhead (same with models/sprites).
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
Kinda as a compromise between an external and removing the applyeffect, I'm trying to add the damage stack system as something you can include on a monster. Ran into some issues. I managed to get around them, but its very hack-ish. I would like your opinion on how the problems were "solved" though.
scriptflags said:
//scriptflags <target> add <name> <type> [value:1] [expiretime:-1] [expiremsg:none]
//- adding a scriptflag with a duplicate name resets its value, and its expire time
//- unless the name starts with "stack", in which case it should have an expire time set
Scriptflags aren't stacking, even with the required name, and I even tried prefixing the type with "stack" as well. It didn't overwrite the expired time though. I worked around this by having an "add" when the scriptflag doesn't exist, and an "edit" when it does exist, overwriting the info that's there.

xdodamage said:
//• "dmgevent:<prefix>"
//- You can use this to setup seperate _dodamage processing events for each attack
//- This will call <prefix>_dodamage, in addition to the usual game_dodamage, on the <attacker>
xdodamage only lets me do <prefix>_dodamage, which returndata can't effect.
returndata said:
//<float> - adjusts damage multipliers inside certain functions (game_damaged, game_takedamage, game_damaged_other)
To get around it, I saved the setdmg and returndata to vards, and set a vard as a flag to tell game_damaged_other when it's ok to do returndata.

Here's what the include looks like:
Code:
//Increasing damage from the same monster via a multiplier. Originally built for the chumtoad. By greatguys1
//Huge contributions from Thothie, thanks

//Damage that you want to stack must have the flag: dmgevent:dmgstk
//Otherwise, this will default to game_damaged, which will stack dots potentially added from addparams from the mapper.

//Accepts params:
//const STACK_FLAG_NAME "stackdmg_generic" //What name will be stored under the scriptflag
//const STACK_MULT_ADD 0.1 //Increment factor for multiplier
//const STACK_MULT_MAX 10 //Max multiplier incrementing can achieve
//const STACK_EXPIRE 5 //How long till the scriptflag expires

#scope server

{
    const STACK_FLAG_NAME "stackdmg_generic"
    const STACK_MULT_ADD 0.1
    const STACK_MULT_MAX 10
    const STACK_EXPIRE 5

    const STACK_ID "stackdmg"

    setvard CALLING_DMGSTK 0
    setvard STACK_ADJUST_DMG 0

    setvard STACK_SETDMG 0
    setvard STACK_RETURNDATA 0
}

{ dmgstk_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt

    setvard CALLING_DMGSTK 1
    dbg "Using dmgstk_dodamage for stack."

    if ( PARAM1 )
    {
        local L_TARGET PARAM2

        if ( $get(L_TARGET,relationship,ent_me) equals enemy ) //didn't hit one of my friends
        {
            local L_PARAM_PASS PARAM6
            callevent do_stack L_TARGET L_PASS_PARAM
        }
    }
}

{ game_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt

    if ( !CALLING_DMGSTK )
    {
        dbg "Using game_dodamage for stack; potentially unwanted; potential problems."

        if ( PARAM1 )
        {
            local L_TARGET PARAM2

            if ( $get(L_TARGET,relationship,ent_me) equals enemy ) //didn't hit one of my friends
            {
                local L_PASS_PARAM PARAM6
                callevent do_stack L_TARGET L_PASS_PARAM
            }
        }
    }
}

{ game_damaged_other //PARAM1=target_hit PARAM2=dmg PARAM3=dmg_type

    if ( STACK_ADJUST_DMG )
    {
        setdmg PARAM3 STACK_SETDMG
        returndata STACK_RETURNDATA

        setvard STACK_ADJUST_DMG 0
    }
}

{ do_stack //PARAM1 = Target PARAM2 = dmgamt

    local L_TARGET PARAM1
    local L_DMG PARAM2

    setvard STACK_ADJUST_DMG 1

    if ( !$get_scriptflag(L_TARGET,STACK_FLAG_NAME,name_exists) )
    {
        scriptflags L_TARGET add STACK_FLAG_NAME STACK_ID 1 STACK_EXPIRE none
    }

    local L_CUR_MULT $get_scriptflag(L_TARGET,STACK_FLAG_NAME,name_value) //find the total value of STACK_FLAG_NAME

    capvar L_CUR_MULT 1 STACK_MULT_MAX //Clamp
  
    setvard STACK_SETDMG $math(multiply,L_DMG,L_CUR_MULT)
    setvard STACK_RETURNDATA L_CUR_MULT

    add L_CUR_MULT STACK_MULT_ADD
    scriptflags L_TARGET edit STACK_FLAG_NAME STACK_ID L_CUR_MULT STACK_EXPIRE none //add stack that decays
    //scriptflags <target> add <name> <type> [value:1] [expiretime:-1] [expiremsg:none]
}
Theoretically, if you set up the consts right, you can have multiple enemies with the stacking damage ability, but with their own multiplier to stack up on. Probably not a good idea to have more than one type of enemy like that attacking you, but it's there.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Bugger and double bugger...

I'll have to set up some tests, but the code is indeed set up so that scriptflags should stack that way... I suppose your workaround is okay... More importantly dmgevent_damaged_other should work that way as well - although looking at the code maybe it's only doing so for registered weapon attacks, which would need to be fixed. Problem comes in where you have, for instance, a critter with this #include and an AOE attack.

You *could* work this in as an applyeffect so a mapper could add it to any monster dynamically. It wouldn't be as bad as the previous setup, as it wouldn't be trying to add the applyeffect with each attack - rather it'd just tack the script on once when the monster was spawned. Granted, there's still the problem that there is no universal way that I can think of to recognize and separate the critter's primary attack from any DOT it may tack on. Would be nice if our mobs were better organized in that fashion, among others, but meh, reworking several thousand monster scripts to uniform them is not gonna be fun.

It *might* be something we can help fix from the DOT end, however, and there's only so many of those. The damage type filter only checks if the string contains "fire", "cold", "lightning", or whatnot, so you can tack on suffixes to that all ya want. Might help us with this sorta problem if all the DOT scripts tacked _dot to the damage type to identify it as such. As it is, they already all add on _effect, so DOT effects don't end up being parried. (This is among the reasons I pointed out to the MSS team that they really need a more sophisticated damage struct, so they don't end up in our boat - too late for us to go back and turn it into a mslist or some such.)

(Also need to fix that skip() problem before we do a release.)
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
You *could* work this in as an applyeffect so a mapper could add it to any monster dynamically.
Before I do that I'm gonna ask, which would you prefer?

(Also need to fix that skip() problem before we do a release.)
What skip problem?

Edit:
Can't do changing pitch in sound if I'm using playrandomsound... changing sounds back into tokens.
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Ya mentioned it in the skip() bug report forums... I need to add it to code requests, along with the dmgevent and scriptflags stack bugs (though I could probably handle those).

...and yeah, I did mean to mention if you want to change pitch you can't use playrandomsound. Again, your token string method is probably is about the same overhead. Also, again, 8-bit waves only. You can check the bits the sound uses with Goldwave, I suppose, though MPC will tell you under Properties.

As for which I'd prefer... I don't see this being a wildly popular monster modification - it'd be a bit unintuitive if it were - rather it'll probably be unique to particular swarm critters, plus it probably wouldn't work right with a lotta critters due to AOE and DOTs. I could see where a variant of it would be good for Scarabs though. In their case, they are already rigged up to increase damage with the more that are latched on (monsters/scarab_fire) - they use a simpler external call (ext_scarab_latch) combined with an applyeffect... Which they probably shouldn't, but they predate the functions that could be used to avoid it.

Should point out that the Scarabs lack AI specifically because they are designed to spawn in large swarms. It's good that you have a low-poly model, but if you intend to have a lot of these running about, something to consider - custom AI is a pain in the butt though. monsters/base_stripped_ai includes externals, but they are bad at navigation and the like.
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
Ya mentioned it in the skip() bug report forums...
oyeah
I've managed to forget about that...

Should point out that the Scarabs lack AI specifically because they are designed to spawn in large swarms. It's good that you have a low-poly model, but if you intend to have a lot of these running about, something to consider - custom AI is a pain in the butt though. monsters/base_stripped_ai includes externals, but they are bad at navigation and the like.
About 5-6 of them are sufficient to kill a 900+ hp character efficiently, though I was thinking of nerfing it so it'd take ~10. I'm thinking that'd be fine as long as its not combined with too many other enemies?

Edit:
I've just realized that the scriptflag doesn't clear on the player's death. I don't know how to go about fixing that without doing something with the externals.
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
I've just realized that the scriptflag doesn't clear on the player's death. I don't know how to go about fixing that without doing something with the externals.
Towards the bottom of player/player_main->{ game_death you will see a list of scriptflags that are cleared. Sadly, you would have to add to that. Though, maybe not a big deal if the scriptflag is set to expire in 5-10 seconds anyways.

I should make a list of scriptflag names and types in use, similar to the list of quest tags at the top of that script.
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
I think the Chumtoad is done. Could you look it over to see if it's ok? I'll also be providing the gibs cleffect I created.
Chumtoad:
Code:
//Chumtoad by greatguys1
//This doens't follow the original concept of it from hl1 what-so-ever.
//The idea is to have it like "zerg" where they're most effective in numbers, having damage scale higher the more it attacks, and even more when more are attacking the same target.

#scope server

{
    //Settings for base monster new
    setvar ANIM_RUN "hop_1"
    setvar ANIM_WALK "hop_1"
    setvar ANIM_IDLE "idle"
    setvard ANIM_ATTACK "flinch2"

    setvard NPC_NO_ATTACK 0
  
    setvard ATTACK_MOVERANGE 10
    setvard ATTACK_RANGE 50
    setvard ATTACK_HITRANGE 70
    const ATTACK_HITCHANCE 80

    const FLEE_HEALTH 0
    const FLEE_CHANCE 0

    setvard CANT_FLEE 1
    setvard CAN_RETALIATE 1
    setvard CAN_FLINCH 0

    //Monster explode
    const EXPLOSION_DAMAGE 65

    //Other stuff particular to this script
    const ANIM_CHARGE "idle2"
    const ANIM_SUICIDE "flinch1"
    setvard ANIM_IDLE3_CHANCE 5 //% chance to use the idle3 animation, slower but more damage
    const LONG_ATTACK 35 //Not long range, but taking longer to strike.
    const SHORT_ATTACK 15

    const MONSTER_HEALTH $get(ent_me,maxhp) //I think this'll grab it again should it change.

    const GIB_BURSTER_SCRIPT "effects/sfx_gib_burst_alien"
    const GIB_BURSTER_FORCE 300

    const EXPLOSION_SFX "effects/sfx_sprite"
    const EXPLOSION_SPRITE "oculus/exp_green.spr"
    const EXPLOSION_SFX_SOUND "monsters/tube/Tube_ExplodingDeath.wav"

    const IDLE_SOUND1 "monsters/ogre_welp/bc_idle2.wav"
    const IDLE_SOUND2 "monsters/ogre_welp/bc_idle3.wav"
    const IDLE_SOUND3 "monsters/ogre_welp/bc_idle4.wav"

    const DEATH_SOUND1 "monsters/bat/pain1.wav"
    const DEATH_SOUND2 "monsters/bat/pain2.wav"
    const DEATH_SOUND3 "monsters/beetle/idle.wav2"
    const DEATH_SOUND4 "monsters/beetle/idle3.wav"

    const PAIN_SOUND1 "monsters/ogre_welp/bc_pain1.wav"
    const PAIN_SOUND2 "monsters/ogre_welp/bc_pain2.wav"
    const PAIN_SOUND3 "monsters/ogre_welp/bc_pain3.wav"

    const HIT_SOUNDS "monsters/tube/TubeCritter_Hit1.wav;monsters/tube/TuberCritter_Hit2.wav;monsters/tube/TubeCritter_Hit3.wav"

    const PITCH_MIN 30
    const PITCH_MAX 200

    setvard SUICIDE_CHANCE 0 //% chance
    const SUICIDE_THRESHOLD 0.7 //Multiplier of what health I need to be under before I check against chance to explode
    const SUICIDE_DISTANCE 100 //Distance affected by explosion
    setvard SUICIDING 0
    setvard TRIED_SUICIDE 0

    const BLINK_FREQ 5
    const BLINK_DUR 0.2 //For each step
    setvard BLINK_STEP 0
    const BLINK_MAX 2 //Two other than the idle, eye open state.
    setvard BLINK_CYCLE 1 //1 for up, -1 for down

    setvard HACKING_ATK_DIST 0

    setvard DO_IDLE_NOISES 1
    const IDLE_NOISE_FREQ 5
}

#include monsters/base_monster_new
#include test_scripts/monster_explode
#include test_scripts/damage_stack

{ game_precache

    precache monsters/chumtoad.mdl
    precache EXPLOSION_SPRITE
    precache agibs.mdl
}

{ npc_spawn

    name "a|Chumtoad"
    race vermin
    hp 300
    setmodel "monsters/chumtoad.mdl"
    width 20
    height 40
    setmoveanim ANIM_RUN
    setidleanim ANIM_IDLE
    blood green
    roam 0

    playanim once ANIM_IDLE
    setvard SOUND_CHANNEL CHAN_VOICE

    callevent BLINK_FREQ do_blinking
    callevent IDLE_NOISE_FREQ play_idle
}

{ play_idle

    if ( DO_IDLE_NOISES )
    {
        playrandomsound SOUND_CHANNEL 10 IDLE_SOUND1 IDLE_SOUND2 IDLE_SOUND3
    }

    callevent IDLE_NOISE_FREQ play_idle
}

{ do_blinking

    local L_PROPS $get(ent_me,renderprops)
    local L_SKIN $get_token(L_PROPS,4)

    setvard BLINK_STEP $math(add,BLINK_STEP,BLINK_CYCLE)

    if ( BLINK_STEP >= BLINK_MAX )
    {
        setvard BLINK_CYCLE -1
    }
    else if ( BLINK_STEP <= 0 )
    {
        setvard BLINK_CYCLE 1
    }

    if ( L_SKIN < 3 ) //In case a mapper changes the skin
    {
        setprop ent_me skin BLINK_STEP
    }

    if ( BLINK_STEP == 0 )
    {
        callevent BLINK_FREQ do_blinking
    }
    else
    {
        callevent BLINK_DUR do_blinking
    }
}

{ game_damaged

    local L_HP $get(ent_me,hp)

    if ( L_HP <= $math(multiply,MONSTER_HEALTH,SUICIDE_THRESHOLD) )
    {
        if ( !TRIED_SUICIDE )
        {
            setvard TRIED_SUICIDE 1

            if ( $rand(1,100) <= SUICIDE_CHANCE )
            {
                callevent do_suicide_charge
            }
        }
    }

    playrandomsound SOUND_CHANNEL 10 PAIN_SOUND1 PAIN_SOUND2 PAIN_SOUND3
}

{ dmgstk_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt
//Thanks Thothie for most of this event

    if ( PARAM1 ) //I hit
    {
        local L_TARGET PARAM2

        setvard HACKING_ATK_DIST 1

        local L_MULT $get_scriptflag(L_TARGET,STACK_FLAG_NAME,name_value)
        subtract L_MULT 0.1 //Upon the first attack, the get scriptflag will always return 1.1, 'cuz the system added it already. I did a 1.0 attack though. Compensating here.
        local L_PITCH $ratio($math(divide,L_MULT,STACK_MULT_MAX),PITCH_MIN,PITCH_MAX) //The math gets a number between 0-1 depending on how much it stacked already.

        playsound SOUND_CHANNEL 10 $get_random_token(HIT_SOUNDS) 0.8 L_PITCH
    }
}

{ game_damaged_other //PARAM1=target_hit PARAM2=dmg PARAM3=dmg_type

    if ( HACKING_ATK_DIST ) //Unfortunately, I don't think this will have effect on the damage stacker without adding another flag. I already have too many. If it missed here, it likely added a stack. Hopefully shouldn't be too detrimental
    {
        local L_HACK_START $get(ent_me,origin)
        local L_HACK_END $get(PARAM1,origin)

        local L_HACK_HITRANGE ATTACK_HITRANGE //Unfortunately need to hack hitrange because of varying enemies width.
        add L_HACK_HITRANGE $math(divide,$get(PARAM1,width),2)

        if ( $get(PARAM1,isplayer) ) //Hack in extra distance with a player because their origin is in the middle of the ent while others are at their feet.
        {
            add L_HACK_HITRANGE $math(divide,$get(PARAM1,height),2)
        }

        if ( $dist(L_HACK_START,L_HACK_END) >= L_HACK_HITRANGE )
        {
            returndata 0
        }
        else if ( $get_traceline(L_HACK_START,L_HACK_END,worldonly) )
        {
            returndata 0
        }

        setvard HACKING_ATK_DIST 0
    }
}

{ game_death

    playrandomsound SOUND_CHANNEL 10 DEATH_SOUND1 DEATH_SOUND2 DEATH_SOUND3 DEATH_SOUND4
}

{ do_suicide_charge

    callevent npcatk_suspend_ai
    playanim critical ANIM_CHARGE
    setvard SUICIDING 1
}

{ finish_charge //Called from the model

    playanim critical ANIM_SUICIDE
}

{ finish_suicide //Called from the model

    setprop ent_me rendermode 5
    setprop ent_me renderamt 0

    callevent do_explode
    callevent npc_suicide
}

{ do_explode

    local L_VEC $get(ent_me,origin)
    vectoradd L_VEC $vec(0,0,30)

    local L_COL $clcol(0,0,0)
    local L_STR "0;3;255;add;"
    stradd L_STR L_COL
    stradd L_STR ";20;10"

    clientevent new all EXPLOSION_SFX L_VEC EXPLOSION_SPRITE L_STR 0.5
    clientevent new all GIB_BURSTER_SCRIPT L_VEC GIB_BURSTER_FORCE
    playsound 0 10 EXPLOSION_SFX_SOUND
}

{ beam_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt

    if ( PARAM1 ) //Hack for explosion to do stack damage. Really getting tired of ""hacking"" all this. This script is gonna turn out a mess.
    {
        local L_TARGET PARAM2

        if ( $get(L_TARGET,relationship,ent_me) equals enemy )
        {
            local L_PASS_PARAM PARAM6
            callevent do_stack L_TARGET L_PASS_PARAM
        }
    }
}

{ npc_selectattack

    if ( $rand(0,100) <= ANIM_IDLE3_CHANCE )
    {
        setvard ANIM_ATTACK idle3
    }
    else
    {
        setvard ANIM_ATTACK flinch2
    }
}

{ attack_1 //Called from the model

    callevent do_attack LONG_ATTACK
}

{ attack_2 //Called from the model

    callevent do_attack SHORT_ATTACK
}

{ do_attack //PARAM1 = damage

    xdodamage NPCATK_TARGET direct PARAM1 ATTACK_HITCHANCE ent_me ent_me none pierce dmgevent:dmgstk
    //Doing direct damage so chumtoads can hit through each other.
}

{ cycle_up

    setvard DO_IDLE_NOISES 0
}

{ cycle_down

    setvard DO_IDLE_NOISES 1
}

{ ext_suicide_chance //PARAM1 = new suicide chance //Intended to be called via the map.

    setvard SUICIDE_CHANCE PARAM1
}

monster_explode:
Code:
//Suicide Explosion base by greatguys1

//Call do_explode from the top script to have the monster explode.
//Initiate any cleffects from a do_explode in the top script.

//Cutomizable vars
// const EXPLOSION_DISTANCE [num] default: 120 "The radius of the explosion"
// const EXPLOSION_DAMAGE [num] default: 300 "How much damage will the explosion do"
// const EXPLOSION_DAMAGE_FALLOFF [0-1] default: 0.2 "How much less damage will do from the center; 1 will do no damage at the very edge of the radius."
// const EXPLOSION_FORCE [num] default: 300 "How much force people caught in the radius will be pushed"
// const EXPLOSION_TYPE [damage type] default: blunt_effect "Which element to use when damaging enemies"

#scope server

{
    const EXPLOSION_DISTANCE 120
    const EXPLOSION_DAMAGE 300
    const EXPLOSION_DAMAGE_FALLOFF 0.2
    const EXPLOSION_FORCE 300
    const EXPLOSION_TYPE blunt_effect
}

{ do_explode

    if ( EXPLOSION_DAMAGE )
    {
        xdodamage $get(ent_me,origin) EXPLOSION_DISTANCE EXPLOSION_DAMAGE EXPLOSION_DAMAGE_FALLOFF ent_me ent_me none EXPLOSION_TYPE dmgevent:beam //<target|(src_origin)> <range|aoe|(dest_origin)|direct> <damage> <cth|fall_off> <attacker> <inflciter> <skill|none> <dmg_type> [flag_string]
    }
}

{ beam_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt //Thanks Thothie!

    if ( PARAM1 )
    {
        local L_TARGET PARAM2

        if ( $get(L_TARGET,relationship,ent_me) equals enemy )
        {
            if ( EXPLOSION_FORCE != 0 )
            {
                local L_YAW $angles($get(ent_me,origin),$get(L_TARGET,origin))
                addvelocity L_TARGET $relvel($vec(0,L_YAW,0),$vec(0,EXPLOSION_FORCE,$math(divide,EXPLOSION_FORCE,1.3)))
            }
        }
    }
}

damage_stack:
Code:
//Increasing damage from the same monster via a multiplier. Originally built for the chumtoad. By greatguys1
//Huge contributions from Thothie, thanks

//Damage that you want to stack must have the flag: dmgevent:dmgstk
//Otherwise, this will default to game_damaged, which will stack dots potentially added from addparams from the mapper.

//Accepts params:
//const STACK_FLAG_NAME "stackdmg_generic" //What name will be stored under the scriptflag
//const STACK_MULT_ADD 0.1 //Increment factor for multiplier
//const STACK_MULT_MAX 10 //Max multiplier incrementing can achieve
//const STACK_EXPIRE 5 //How long till the scriptflag expires

#scope server

{
    const STACK_FLAG_NAME "stackdmg_generic"
    const STACK_MULT_ADD 0.1
    const STACK_MULT_MAX 10
    const STACK_EXPIRE 5

    const STACK_ID "stackdmg"

    setvard CALLING_DMGSTK 0
    setvard STACK_ADJUST_DMG 0

    setvard STACK_SETDMG 0
    setvard STACK_RETURNDATA 0
}

{ dmgstk_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt

    setvard CALLING_DMGSTK 1
    dbg "Using dmgstk_dodamage for stack."

    if ( PARAM1 )
    {
        local L_TARGET PARAM2

        if ( $get(L_TARGET,relationship,ent_me) equals enemy ) //didn't hit one of my friends
        {
            local L_PASS_PARAM PARAM6
            callevent do_stack L_TARGET L_PASS_PARAM
        }
    }
}

{ game_dodamage //PARAM1=hit:0|1 PARAM2=ent_hit PARAM3=(start) PARAM4=(end) PARAM5=DmgType PARAM6=DmgAmt

    if ( !CALLING_DMGSTK )
    {
        dbg "Using game_dodamage for stack; potentially unwanted; potential problems."

        if ( PARAM1 )
        {
            local L_TARGET PARAM2

            if ( $get(L_TARGET,relationship,ent_me) equals enemy ) //didn't hit one of my friends
            {
                local L_PASS_PARAM PARAM6
                callevent do_stack L_TARGET L_PASS_PARAM
            }
        }
    }
}

{ game_damaged_other //PARAM1=target_hit PARAM2=dmg PARAM3=dmg_type

    if ( STACK_ADJUST_DMG > 0 )
    {
        setdmg PARAM3 STACK_SETDMG
        returndata STACK_RETURNDATA

        subtract STACK_ADJUST_DMG 1
    }
}

{ do_stack //PARAM1 = Target PARAM2 = dmgamt

    local L_TARGET PARAM1
    local L_DMG PARAM2

    add STACK_ADJUST_DMG 1 //Hopefully will take care of the case of AoE attacks.

    if ( !$get_scriptflag(L_TARGET,STACK_FLAG_NAME,name_exists) )
    {
        scriptflags L_TARGET add STACK_FLAG_NAME STACK_ID 1 STACK_EXPIRE none
    }

    local L_CUR_MULT $get_scriptflag(L_TARGET,STACK_FLAG_NAME,name_value) //find the total value of STACK_FLAG_NAME

    capvar L_CUR_MULT 1 STACK_MULT_MAX //Clamp
  
    setvard STACK_SETDMG $math(multiply,L_DMG,L_CUR_MULT)
    setvard STACK_RETURNDATA L_CUR_MULT

    add L_CUR_MULT STACK_MULT_ADD
    scriptflags L_TARGET edit STACK_FLAG_NAME STACK_ID L_CUR_MULT STACK_EXPIRE none //add stack that decays
    //scriptflags <target> add <name> <type> [value:1] [expiretime:-1] [expiremsg:none]
}

sfx_gib_base_burst:
Code:
//Gib burst by greatguys1
//This is meant to be #included into cleffects that make use of the options provided. There are no defaults where media is required.
//Takes inspiration from sfx_explode.

#scope client

{
    const GIB_MODEL none //[model name] Which gib model to use
    const GIB_FORCE_SUBMODEL none //[idx] Force a submodel; don't override if you wish to use random submodels
    const GIB_SUBMODELS none //[num] How many submodels does it have? Not needed if forcing a submodel

    const GIB_AMT 5 //[num] How many gibs do you want

    const GIB_TIME 6 //[num] How long do gibs stay alive
    const GIB_BOUNCE_FACTOR 1.3 //[0-1] A float of the bounce multiplier.
    const GIB_GRAV 0.7 //[num] Multiplier for gravity, 1 is normal.
    const GIB_SCALE 1 //[num] Multiplier for scale. 1 is normal size
    const GIB_COLLISION "world" //["world"|"all"|"none"] What to collide with.
    const GIB_DIE_ON_COLLIDE 0 //[0|1] Die on collision? Must also be used with "world" or "all".
    const GIB_RENDER_MODE "normal" //["normal"|"color"|"texture"|"glow"|"alpha"|"add"] Too long to list, refer to scripting_all_docs under cleffect
    const GIB_RENDER_FX "normal" //["normal"|"glow"|"player"] --------------------------^
    const GIB_RENDER_AMT 0 //Amount of effect applied by rendermode and/or renderfx
    const GIB_COLOR 0 //(r,g,b) color
    const GIB_FADEOUT "lifetime" //How long to take till it fades out, usually should be equal to its lifetime.
    const GIB_SPIN_MULT 1 //[num] Multiplier for how fast the gibs spin while in the air.

    const GIB_SPIN_MAX 5
    const MAX_ANG 359
}

{ client_activate //<origin> <force>

    setvard FX_CENTER PARAM1
    setvard FX_FORCE PARAM2

    calleventloop GIB_AMT create_gibs

    callevent 3.0 remove_me
}

{ create_gibs

    cleffect tempent model GIB_MODEL FX_CENTER setup_gib update_gib
}

{ update_gib
  
}

{ setup_gib

    local L_PITCH $rand(0,MAX_ANG)
    local L_YAW $rand(0,MAX_ANG)
    local L_ROLL $rand(0,MAX_ANG)

    local L_ANG $vec(L_PITCH,L_YAW,L_ROLL)

    local L_TOTAL 0
    add L_TOTAL L_PITCH
    add L_TOTAL L_YAW
    add L_TOTAL L_ROLL

    local L_VECX $math(multiply,$math(divide,L_PITCH,L_TOTAL),FX_FORCE) //Ratio doesn't wanna fucking work, but this does essentially the same thing.
    local L_VECY $math(multiply,$math(divide,L_YAW,L_TOTAL),FX_FORCE)
    local L_VECZ $math(multiply,$math(divide,L_ROLL,L_TOTAL),FX_FORCE)

    local L_VEC2 $vec(L_VECX,L_VECY,L_VECZ)

    local L_VEL $relvel(L_ANG,L_VEC2)
  
    cleffect tempent set_current_prop death_delay    GIB_TIME
    cleffect tempent set_current_prop bouncefactor    GIB_BOUNCE_FACTOR
    cleffect tempent set_current_prop gravity    GIB_GRAV
    cleffect tempent set_current_prop scale        GIB_SCALE
    cleffect tempent set_current_prop collide    GIB_COLLISION

    if ( GIB_DIE_ON_COLLIDE )
    {
        cleffect tempent set_current_prop collide "die"
    }

    cleffect tempent set_current_prop rendermode    GIB_RENDER_MODE                 
    cleffect tempent set_current_prop renderfx    GIB_RENDER_FX             
    cleffect tempent set_current_prop renderamt    GIB_RENDER_AMT

    if ( GIB_COLOR )
    {
        cleffect tempent set_current_prop rendercolor GIB_COLOR
    }

    cleffect tempent set_current_prop fadeout    GIB_FADEOUT
  
    if ( GIB_FORCE_SUBMODEL )
    {
        cleffect tempent set_current_prop body    GIB_FORCE_SUBMODEL
    }
    else
    {
        cleffect tempent set_current_prop body    $rand(0,$math(subtract,GIB_SUBMODELS,1))
    }

    cleffect tempent set_current_prop velocity L_VEL

    cleffect tempent set_current_prop angles L_ANG //An unintentional effect of this is that it appears to select a random angle again after hitting the ground. Still works out ok though.
}

{ remove_me
    removescript
}

sfx_gib_burst_alien:
Code:
//Alien Gib Burst by greatguys1

#scope client

{
    const GIB_MODEL "agibs.mdl" //[model name] Which gib model to use //Turns out it must be included manually in the summmoning script; precachefile doens't work here.
    const GIB_SUBMODELS 4 //[num] How many submodels does it have? Not needed if forcing a submodel
    const GIB_AMT 5
}

#include effects/sfx_gib_base_burst

Also add this to your player/player_main on line 486:
Code:
scriptflags ent_me cleartype stackdmg

Or otherwise, all in a nice RAR:
https://www.dropbox.com/s/vnq3nwe60aisruz/Chumtoad and Scripts.rar?dl=1

The gibs must be compiled with sc.dll, otherwise they don't work. Chumtoad is currently rigged to have itself, damage_stack, and monster_explode all in the test scripts. If you compile it all into the sc.dll, you'll have to change the includes. Remember to change the player/player_main even if you use the RAR.

Lots of hacking was done with the attack to ignore entities that are in the way... if stuff gets difficult to understand you can ask here and I'll attempt to explain what I was doing.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
The gibs must be compiled with sc.dll, otherwise they don't work.

Client side scripting isn't working for test_scripts? Bugger...

There is an effects gibs command, but this is certainly more optimal, as those gibs are all tracked server side. It'd become a problem if there were a lotta explosions.

I'll try to make sure <dmgevent>_damaged_other and stack type scriptflags work proper next patch, but I dun think those fixes will make it worth re-imagining this.
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
I'll try to make sure <dmgevent>_damaged_other and stack type scriptflags work proper next patch, but I dun think those fixes will make it worth re-imagining this.
It'd certainly help being able to read the script better. I'd redo some stuff here once it's fixed.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Integrating this...

Renaming/redirecting damage_stack.script and monster_explode.script to monsters/base_damage_stack.script and monsters/base_monster_explode.script respectively.

Placing sfx_ files in effects/ - which seems to be the intent, but ya provided no folder structure.

Would test but... Where's the model for this thing again?
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
Chumtoad model:
https://www.dropbox.com/s/lgc4q7crntkwhl0/chumtoad.mdl?dl=1

Source:
https://www.dropbox.com/s/23r4qadhhkuc79c/chumtoad source.rar?dl=1

The source is a decompiled chumtoad with the extra skins and events tied to some frames of animation for attacking and exploding.

Those are the intended folders. Reminder to change the includes on the chumtoad. I intended to have SUICIDE_CHANCE at 5, but forgot to change it back after testing the addparam.

Edit: Chumtoad model goes in models/monsters
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Got to this awhile ago, but kept forgetting to write it up. :oops:

Few things - the damage stack effect doesn't seem to be working.

[video]https://www.msremake.com/attachments/chumtoad_dmgstack-mp4.5619/[/video]
(Not sure if I messed up something...)

While they can attack while being stood upon, only three appear to be able to at a time. You might wanna shorten them, thus restoring their ability to crawl over each other. (They operate adorably with height set to 8.)

You want to do the traceline check BEFORE you run the attack, not in _damaged_other. This both to save processing time, and prevent the same sorta "false hit" problem I just fixed in the ice skeletons. (Mind the "setdmg hit 0" in _damaged_other also fixes this in other situations.) This also resolves the issue that they will currently add a dmgstack, even if they don't hit.

I have fixed the [dmgevent]_damaged_other function (mind, this calls both this event and the standard "game_damaged_other"). Hopefully the lack of stacking isn't because of that, though I've yet to trace it out.
Edit: Damnit, was afraid of that. Seems calling a second _damaged_other event is indeed why the damage stacking wasn't working - so not your fault. Hrmm... Figuring a way around that is gonna be a bit of a bugger.

Scriptflag stacking seems to already be working - are you sure you prefixed the scriptflag's name with "stack", and not its type?

To test this, I altered player/externals->ext_sflag_test to:
Code:
{ ext_sflag_test
    scriptflags ent_me add stacktest cold 1 10.00 "Test1-Cold+1 Expired"
    scriptflags ent_me add stacktest cold 1 10.00 "Test2-Cold+2 Expired"
    scriptflags ent_me add stacktest cold 1 10.00 "Test2-Cold+3 Expired"
    scriptflags ent_me add stacktest cold 1 10.00 "Test2-Cold+4 Expired"
    scriptflags ent_me add test3 fire 4 5.0 "Test3-Fire+3 Expired"
    scriptflags ent_me add test4 fire -1 11.0 "Test4-Fire-1 Expired"
    callevent ext_sflag_test_loop
}
...Ran ". eventme ext_sflag_test" a few times in a row, monitoring it in the event hud and via ". eventme ext_showflags". Seemed to check out.
 

Attachments

  • chumtoad_dmgstack.mp4
    5.9 MB · Views: 109
  • chumtoad_omgkewt.webm
    2.3 MB · Views: 16

Kanta

Old Skool Apostle
Alpha Tester
Joined
Jan 24, 2013
Messages
638
Reaction score
89
Location
ms_swamp
Precious babies.

Chumtoad charm please?
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Might be better as an Affliction Spell or a throwable potion (Bottle of Choads). The model is simple enough that it might be integratable into something already precached. Hrmm...

@greatguys1 - As edited above, turns out the damage stack failing was not your fault... Adding a dmgevent_damaged_other is causing issues when both it and the regular one go off. Maybe I could instead pass the dmgevent name to game_damaged_other, so you could conditionally control it from there instead. Lookin' into it.

edit: Yes, seems that'll work.

PS. Also, these things are deaf. Add "hearingsensitivity" property - 11 is max (responds to non-crawling foot steps or combat sounds at about 512 units - crawling at 64.)
 
Last edited:

Kanta

Old Skool Apostle
Alpha Tester
Joined
Jan 24, 2013
Messages
638
Reaction score
89
Location
ms_swamp
I am 100% on board for new spells of any kind, good ✔✔✔✔✔ shit ✔✔ go౦ԁ sHit thats ✔ some goodshit rightthere.

So yeah that'd be cool too.
 
Last edited:

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
@greatguys1 - Ah, figured out why your scriptflags weren't stacking - you unwittingly used the wrong function on the reading side.

$get_scriptflags(<target>,<type>,type_value) is the one ya want, not <name>,name_value, as that'll just return the first value of <name>.

Which is my fault, as you can use "totalvalue", in place of "type_value", and I shoulda had it that way in the ms.stx, as it's more intuitive...

But if ya look at the $get_scriptflags code:
Code:
    if ( Params[2] == "totalvalue" || Params[2] == "type_value" )
    {
        sf_not_found_msg = "none";
        sf_add_values = true;
    }
[...]
    else if ( Params[2] == "name_value" )
    {
        sf_not_found_msg = "none";
        sf_get_by_name = true;
        sf_get_value_by_name = true;
    }

Only type_value has sf_add_values. Granted, I could work it up so you could do it either way, though I dunno if I wanna fiddle with that code again after working so hard to make it work just right.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
Here's my WIP version of the allied one for use with the aforementioned Bottle of Choads. I figure players will stack up enough of them, and use them to help take down bosses, so they are dark damage and fairly mean. Maybe spawn 3-5 per bottle, depending on your related spellcasting skill.

This is using a new optimized AI I cooked up, but conventions are pretty close.

Code:
#scope server
//task list:
//- Figure how many bites these guys get on average and change self-destruct timer to #bites (~40 bites)

//For Bottle of Choads by Thothie
//self destruct after 20 seconds after engaging, two minutes if no engagement

//probably going to new new anims for throwable potions, and new model for this one
//probably track throwable ammo via quest data

{
	setvar CHOAD_DEV_TEST 0 //makes hostile and builds stats based on G_PLAYER_DEV

	//** might change for megachode or other choad types

	//used by minai
	setvar ANIM_RUN "hop_2"
	setvar ANIM_WALK "hop_1"
	setvar ANIM_IDLE "idle"
	setvard ANIM_ATTACK "flinch2"
	if ( !CHOAD_DEV_TEST ) setvard NPC_NO_PLAYER_DMG 1
	setvard ATTACK_MOVERANGE 1 //**
	setvard ATTACK_RANGE 50 //**
	setvard NPC_WALK_RANGE 64 //**

	//custom below
	setvar ANIM_DEATH die3
	setvar ANIM_EXPLODE flinch1
	setvard ATTACK_HITRANGE 70 //**
	const ATTACK_HITCHANCE 80

	const ANIM_CHARGE idle2
	const ANIM_SUICIDE flinch1
	const ANIM_ATTACK_STRONG idle3
	const ANIM_ATTACK_NORMAL flinch2
	const PITCH_MIN 30 //**
	const PITCH_MAX 200 //**

	const CHOAD_BLINKS 1 //**
	const EYE_SEQUENCE "0;1;2;1;0"

	const CHANCE_ATTACK_STRONG 5$ //**

	//x DMG_BASE is a setvard of $get(MY_OWNER,skill.spellcasting.affliction)
	const DMG_ATTACK_NORMAL $math(multiply,DMG_BASE,0.3) //**
	const DMG_ATTACK_STRONG $math(multiply,DMG_BASE,0.6) //**
	const DMG_EXPLODE $math(multiply,DMG_BASE,8.0) //**
	const BASE_HP $math(multiply,DMG_BASE,20.0) //**

	const DMG_TYPE dark

	const DMGSTACK_MULTI 0.5 //additional damage multiplier per stack
	const DMGSTACK_MAX_STACKS 15 //maximum number of stacks
	const DMGSTACK_EXPIRE 5.0 //stack life


	const BASE_LIFETIME_MAX 120.0 //**
	const BASE_LIFETIME_COMBAT $randf(20.0,30.0) //**

	const XP_SKILL spellcasting.affliction //maybe different for other variants **

	const FREQ_IDLE_NOISE $randf(4.0,6.0)

	//for base_trade_xp
	const TXP_PLAYER MY_OWNER 
	const TXP_SKILL XP_SKILL
	const TXP_AUTOMATIC 0

	//explosion fx data
	const EXPLOSION_SFX effects/sfx_explodecloud_fancy
	//const EXPLOSION_SPRITE "bigsmoke.spr"
	const EXPLODE_SPRITE_COLOR (128,0,128) //**
	const EXPLODE_SPRITE_SCALE 1.0 //**
	const RAD_EXPLODE 128 //**
	const VOFS_EXPLODE $vec(0,0,30) //might tweak for larger choads **
	const PITCH_EXPLODE 100 //pitch of explode prep sound **
	const EXPLODE_PUSH_FORCE 400 //force of explosion push **

	//other media
	const SOUND_EXPLOSION1 "houndeye/he_blast1.wav"
	const SOUND_EXPLOSION2 "houndeye/he_blast2.wav"
	const SOUND_EXPLOSION3 "houndeye/he_blast3.wav"
	const SOUND_IDLE1 "monsters/ogre_welp/bc_idle2.wav"
	const SOUND_IDLE2 "monsters/ogre_welp/bc_idle3.wav"
	const SOUND_IDLE3 "monsters/ogre_welp/bc_idle4.wav"
	const SOUND_DEATH1 "monsters/bat/pain1.wav"
	const SOUND_DEATH2 "monsters/bat/pain2.wav"
	const SOUND_DEATH3 "monsters/beetle/idle.wav"
	const SOUND_DEATH4 "monsters/beetle/idle3.wav"
	const SOUND_PAIN1 "monsters/ogre_welp/bc_pain1.wav"
	const SOUND_PAIN2 "monsters/ogre_welp/bc_pain2.wav"
	const SOUND_PAIN3 "monsters/ogre_welp/bc_pain3.wav"
	const SOUND_VICTORY1 monsters/beetle/idle2.wav
	const SOUND_VICTORY2 monsters/beetle/idle3.wav
	const SOUND_VICTORY3 monsters/beetle/idle5.wav
	const TOKEN_EXPLODE_PREP_SOUNDS "houndeye/he_attack1.wav;houndeye/he_attack2.wav;houndeye/he_attack3.wav"
	const TOKEN_HIT_SOUNDS "monsters/tube/TubeCritter_Hit1.wav;monsters/tube/TuberCritter_Hit2.wav;monsters/tube/TubeCritter_Hit3.wav"
}

#include monsters/base_minai
#include monsters/summon/base_trade_xp

{ game_dynamically_created //<owner> [hostile:0|1] [level]
	setvard MY_OWNER PARAM1
	setvard AM_SUMMONED 1
	if ( PARAM2 )
	{
		race hostile1
		setvard CHOAD_DEV_TEST 1 
		callevent 0.1 choad_hostile //seems setvar comes after game_dynamically_created, so redundancy
	}

	if ( !$get(MY_OWNER,isalive) ) //ya dun summoned me with no params, ya jerk
	{
		if ( G_DEVELOPER_MODE )
		{
			setvard MY_OWNER G_DEV_PLAYER
		}
		else
		{
			setvard MY_OWNER GAME_MASTER
		}
	}

	local L_BASE_SKILL_GET skill.
	stradd L_BASE_SKILL_GET XP_SKILL
	setvard DMG_BASE $get(MY_OWNER,L_BASE_SKILL_GET)

	if ( !$get(MY_OWNER,isplayer) ) setvard DMG_BASE 45

	if ( PARAM3 > 0 )
	{
		setvard DMG_BASE PARAM3
	}

	hp BASE_HP
	if MY_OWNER isnot GAME_MASTER //if true, assume I'm a world spawn, so we don't wanna explode in two minutes
	local L_OWNER_CONC $get(MY_OWNER,stat.concentration)
	local L_MAX_LIFE $math(add,BASE_LIFETIME_MAX,L_OWNER_CONC)
	callevent L_MAX_LIFE do_explode
}

{ npcatk_get_attribs_spawn
	if CHOAD_DEV_TEST
	local L_BASE_XP $math(multiply,DMG_BASE,8.0)
	capvar 1 1000
	local L_DIM_RATIO $ratio($math(divide,L_BASE_XP,1000),0,400) //xp to remove as we get closer to max
	multiply L_DIM_RATIO 0.5
	subtract L_BASE_XP L_DIM_RATIO
	skilllevel L_BASE_XP
}

{ choad_hostile //external, in case you prefer to use this mob over the regular monsters/chumtoad
	dbg choad_hostile
	race hostile1
	setvard CHOAD_DEV_TEST 1
}

{ game_spawn
	callevent choad_spawn
	callevent 0.1 set_non_summon_stats
}

{ set_non_summon_stats
	if !AM_SUMMONED
	setvard DMG_BASE 45
	hp BASE_HP
	setvard MY_OWNER GAME_MASTER
	callevent choad_hostile
}

{ choad_spawn
	name "a|Chumtoad"
	if ( CHOAD_DEV_TEST )
	{
		race hostile1
	}
	else
	{
		race human
	}
	setmodel "monsters/chumtoad.mdl" //swap with merged model
	width 20
	height 8
	setmoveanim ANIM_RUN
	setidleanim ANIM_IDLE
	blood green
	roam 0
	hearingsensitivity 11

	catchspeech play_dead play dead

	//start invisible until we hit ground or min fall time is up
	callevent set_fade_in
}

{ npcatk_new_target
	roam 1
}

{ npc_victory
	playrandomsound 0 10 SOUND_VICTORY1 SOUND_VICTORY2 SOUND_VICTORY3
	if game.time > NEXT_VICTORY_ANIM
	setvard NEXT_VICTORY_ANIM $math(add,game.time,5.0)
	playanim once flinch1
}

{ play_dead
	playanim critical playdead2
	playrandomsound 0 10 SOUND_DEATH1 SOUND_DEATH2 SOUND_DEATH3 SOUND_DEATH4
}

{ go_visible
	setprop ent_me rendermode 0
	setprop ent_me renderamt 255
}

{ npc_fadein_done
	setvard DID_MANIFEST 1
}

{ npcatk_hunt

	local L_GAME_TIME game.time

	if ( !DID_MANIFEST )
	{
		//shortens the fade in to as soon as I'm on the ground (if I'm fully inititated)
		if NPC_RENDER_AMT < 255
		if $get(ent_me,onground)
		setvard NPC_RENDER_AMT 255
		callevent go_visible
		callevent npc_fadein_done
	}

	if ( CHOAD_BLINKS )
	{
		if L_GAME_TIME > NEXT_BLINK
		setvard NEXT_BLINK $randf(3.0,10.0)
		add NEXT_BLINK L_GAME_TIME
		setvard CHOAD_EYE_FRAME 0
		callevent choad_blink
	}

	if ( !CHOAD_SUICIDE_COUNTDOWN )
	{
		//set timer to explode if I initiated combat
		//considering switching this to X# bites
		if NPCATK_TARGET isnot unset
		setvard CHOAD_SUICIDE_COUNTDOWN 1
		callevent BASE_LIFETIME_COMBAT do_explode
	}

	if NPCATK_TARGET equals unset

	//target owner's target, if enemy and I don't have one
	local L_OWNER_TARG $get(MY_OWNER,target)
	if ( $get(L_OWNER_TARG,isalive) )
	{
		if $get(L_OWNER_TARG,relationship,ent_me) equals enemy
		callevent npcatk_settarget L_OWNER_TARG
	}

	if NPCATK_TARGET equals unset //might have been set above

	//idle sounds
	if ( L_GAME_TIME > NEXT_IDLE_NOISE )
	{
		setvard NEXT_IDLE_NOISE $math(add,L_GAME_TIME,FREQ_IDLE_NOISE)
		playrandomsound 0 10 SOUND_IDLE1 SOUND_IDLE2 SOUND_IDLE3
	}

	if !CHOAD_DEV_TEST
	//when idle for a bit, follow owner
	if ( L_GAME_TIME > $math(add,NPC_LAST_HAD_TARGET,5.0) )
	{
		callevent npcatk_setmovedest MY_OWNER 128
	}
}

{ choad_blink
	add CHOAD_EYE_FRAME 1
	setprop ent_me skin $get_token(EYE_SEQUENCE,CHOAD_EYE_FRAME)
	if ( CHOAD_EYE_FRAME < $math(subtract,$get_token_amt(EYE_SEQUENCE),1) ) callevent 0.2 choad_blink
}

{ npc_selectattack

	if ( $rand(0,100) <= CHANCE_ATTACK_STRONG )
	{
		setvard ANIM_ATTACK ANIM_ATTACK_STRONG
	}
	else
	{
		setvard ANIM_ATTACK ANIM_ATTACK_NORMAL
	}
}

{ attack_1 //Called from the model

	callevent do_attack DMG_ATTACK_STRONG
}

{ attack_2 //Called from the model

	callevent do_attack DMG_ATTACK_NORMAL
}

{ game_death
	callexternal MY_OWNER ext_summon_died $get(ent_me,itemname)
	if !DID_EXPLODE
	playanim critical ANIM_DEATH
	playrandomsound 0 10 SOUND_DEATH1 SOUND_DEATH2 SOUND_DEATH3 SOUND_DEATH4
	if ( CHOAD_BLINKS ) setprop ent_me skin 2
}

{ game_damaged

	playrandomsound 0 10 SOUND_PAIN1 SOUND_PAIN2 SOUND_PAIN3

	if $get(MY_OWNER,isplayer)
	if !CHOAD_DEV_TEST
	if !DID_EXPLODE
	if !NPC_HIDE_HBAR
	callexternal MY_OWNER ext_show_hbar_monster $get(ent_me,id) 1
}

{ do_attack //PARAM1 = damage
	local L_TRACE_START $get(ent_me,origin)
	local L_TRACE_END $get(NPCATK_TARGET,origin)

	if ( $get(NPCATK_TARGET,isplayer) )
	{
		vectoradd L_TRACE_START z 38
	}

	local L_TRACELINE $get_traceline(L_TRACE_START,L_TRACE_END,worldonly)
	if L_TRACELINE equals L_TRACE_END

//	if ( CHOAD_DEV_TEST )
//	{
//		xdodamage NPCATK_TARGET direct PARAM1 ATTACK_HITCHANCE ent_me ent_me none DMG_TYPE dmgevent:dmgstk
//	}
//	else
//	{
//		xdodamage NPCATK_TARGET direct PARAM1 ATTACK_HITCHANCE MY_OWNER MY_OWNER XP_SKILL DMG_TYPE dmgevent:*dmgstk
//	}

	xdodamage NPCATK_TARGET direct PARAM1 ATTACK_HITCHANCE ent_me ent_me none DMG_TYPE dmgevent:dmgstk
}

//{ dmgstk_dodamage //just testin somethin
//	dbg dmgstk_dodamage 
//}

{ game_damaged_other //PARAM1=target_hit PARAM2=dmg PARAM3=dmg_type PARAM4=dmgevent
	if ( PARAM4 !contains dmgstk )
	{
		if !CHOAD_DEV_TEST
		if ( $get(MY_OWNER,isplayer) ) callevent txp_add_hit $pass(PARAM1) $pass(PARAM2) DMG_TYPE
	}
	else
	{
		add CHOAD_HITS 1 //thinking of using this as exploding counter, rather than the timer
		
		local L_DMG_STACKS $get_scriptflag(PARAM1,choad,type_value)
		capvar L_DMG_STACKS 1 DMGSTACK_MAX_STACKS

		local L_DMG_MULTI 1
		add L_DMG_MULTI $math(multiply,L_DMG_STACKS,DMGSTACK_MULTI)
		local L_DMG $math(multiply,PARAM2,L_DMG_MULTI)
		
		setdmg dmg L_DMG
		returndata L_DMG_MULTI

		//apply stack
		//			<target> add <name>		<type> [value:1] [expiretime:-1] [expiremsg:none]
		scriptflags PARAM1	 add stackchoad	choad  1         DMGSTACK_EXPIRE

		local L_DMG_STACKS $get_scriptflag(PARAM1,choad,type_value)
		local L_PITCH $ratio($math(divide,L_DMG_STACKS,DMGSTACK_MAX_STACKS),PITCH_MIN,PITCH_MAX)

		playsound 0 10 $get_random_token(TOKEN_HIT_SOUNDS) 0.8 L_PITCH

		setvard NPC_DELAY_NEXT_ATTACK_UNTIL $math(add,game.time,$randf(0,0.25)) //delaying attack so he can get closer, if need be

		//report damage and figure XP
		//(dump on kill or death)
		if !CHOAD_DEV_TEST
		if ( $get(MY_OWNER,isplayer) ) callevent txp_add_hit $pass(PARAM1) L_DMG DMG_TYPE
	}
}

{ do_explode
	setvard DID_EXPLODE 1 //tells game_death not to play normal routine
	setvard NPC_HIDE_HBAR 1

	roam 0
	callevent npcatk_suspend_ai -1 explodin
	setmovedest none
	setanim.framerate 0.25
	playanim critical die3 //flinch1 goes wonky when scaled
	setvard CHOAD_SCALE NPC_SCALE
	setvard CHOAD_INFLATE_LOOP 1
	callevent choad_inflate_loop
	setprop ent_me rendermode 15
	callevent 2.5 do_explode2
	playsound 0 10 $get_random_token(TOKEN_EXPLODE_PREP_SOUNDS) 0.8 PITCH_EXPLODE
}

{ choad_inflate_loop
	callevent 0.01 choad_inflate_loop //don't panic, it'll stop when he dies
	add CHOAD_SCALE 0.05
	setprop ent_me scale CHOAD_SCALE
}

{ do_explode2

	local L_EXPLODE_ORG $get(ent_me,origin)
	vectoradd L_EXPLODE_ORG VOFS_EXPLODE

	if ( CHOAD_DEV_TEST )
	{
		dbg rad RAD_EXPLODE dmg DMG_EXPLODE
		xdodamage L_EXPLODE_ORG RAD_EXPLODE DMG_EXPLODE 0.01 ent_me ent_me none DMG_TYPE dmgevent:choadexplode	
	}
	else
	{
		xdodamage L_EXPLODE_ORG RAD_EXPLODE DMG_EXPLODE 0.01 MY_OWNER MY_OWNER XP_SKILL DMG_TYPE dmgevent:choadexplode
	}


	//should make this into its own effect to spare the client script
	//fadeout:0|1;scale;renderamt;rendermode;rendercolor;framerate;frames
//	local L_RENDER_PROPS 1 //fadeout
//	token.add L_RENDERPROPS EXPLODE_SPRITE_SCALE //scale
//	token.add L_RENDERPROPS 200 //renderamt
//	token.add L_RENDERPROPS add //rendermode
//	token.add L_RENDERPROPS EXPLODE_SPRITE_COLOR //rendercolor
//	token.add L_RENDERPROPS 20 //framerate
//	token.add L_RENDERPROPS 17 //frames
//	clientevent new all EXPLOSION_SFX L_EXPLODE_ORG EXPLOSION_SPRITE L_RENDERPROPS 0.5
//	token.set L_RENDERPROPS 1 $math(multiply,EXPLODE_SPRITE_SCALE,0.8)
//	token.set L_RENDERPROPS 3 texture
//	token.set L_RENDERPROPS 4 (255,255,255)
//	clientevent new all EXPLOSION_SFX L_EXPLODE_ORG EXPLOSION_SPRITE L_RENDERPROPS 0.5
	clientevent new all EXPLOSION_SFX L_EXPLODE_ORG EXPLODE_SPRITE_COLOR EXPLODE_SPRITE_SCALE

	//clientevent new all GIB_BURSTER_SCRIPT L_EXPLODE_ORG GIB_BURSTER_FORCE //can't spare the preache here
	playrandomsound 0 10 SOUND_EXPLOSION1 SOUND_EXPLOSION2 SOUND_EXPLOSION3

	setprop ent_me rendermode 5
	setprop ent_me renderamt 0
	blood none

	callevent 0.1 do_explode_remove
}

{ do_explode_remove
	callevent npc_suicide
}

{ choadexplode_dodamage
	if PARAM1
	local L_TARG PARAM2
	if $get(L_TARG,relationship,ent_me) equals enemy
	callevent ext_repel L_TARG $get(ent_me,origin) EXPLODE_PUSH_FORCE 10 $math(multiply,$get(MY_OWNER,maxhp),3.0) //new external
}

The damage/skill pass may need some tweaking, just about to test (so far, I've been working with hostile ones). I know I can do it by processing it on the player's side, if need be, just hoping that won't be the case.

The math is a little different as I'm using stacks with a value of one, and calculating the damage based on that - cuz it was just easier. They don't get to quite as high a damage rating, but get to their max much quicker, though I tried to balance it out so that, at skill level 45, they are a bit stronger than the regular ones. (In this variant, they just explode when their timer is up, which ticks faster if they are in combat.)

PS. If your sprite is coming out the wrong color, try nixing the $clcol() - I don't think sprites need it, as unlike beams and some other cleffects, they don't let you set their alpha through there.
 

greatguys1

Epic Adventurer
MSC Developer
Warriors of the North
MSC Archivist
Joined
Apr 20, 2013
Messages
338
Reaction score
60
Age
26
Location
Yes
Sorry for being absent here, I wanted to wait until I can actually get to all this but I feel like I should at least acknowledge it. I'll probably leave the chumtoad alone until I get Oyster's other monster done, then I'll try and go through and apply your recommendations, but I shouldn't do anything here until I get a personal project done, which has a deadline of next week. After the 11th, I can go through and give a proper reply to everything. Thanks for looking over the script and troubleshooting, I really appreciate it.
 

Thothie

Administrator
Staff member
Administrator
Moderator
MSC Archivist
Joined
Apr 8, 2005
Messages
16,342
Reaction score
326
Location
lost
NP - your script is functional as is, so at least Oyster can work with it.

I've run into an issue with crediting damage on the allied version though... It works, but the problem is, the monster he's attacking blames me for the damage, so it goes after me instead of him.

Options being:
• I just live with that, and make it do a lot more damage and be shorter lived...

• I try to rig up some hacky code thing where he sends a message to his target telling it to attack him instead (which could be exploitable as heck)...

• I try to rig up the code so it passes XP from mob to player, as the "setexpowner" command, which has been broken since before I got here, is supposed to do. ...and somehow pull it off, when no one else who has tried has in over a decade.

• I try to track XP via a var, scriptside, by percentage of damage done to maxhp vs. the mob's xp value, and pass that back to the player via givexp when a mob or the toad dies, and report any hits the choad does directly to the owner's combat hud.

Any other ideas?

I'd hate to come so far only to have to dump the concept and move onto something else, though I suppose the other throwable flasks I had in mind wouldn't run into this issue, being basically mega bombs.
 

Kanta

Old Skool Apostle
Alpha Tester
Joined
Jan 24, 2013
Messages
638
Reaction score
89
Location
ms_swamp
Isn't that exactly how the snake staff works though? Where it's you attacking the enemy "through" the snake and you get XP for it.
 
Top