Fireball problems

Users who are viewing this thread

Koozer

Regular
I've created a few spell-type items (modified from this thread : http://forums.taleworlds.com/index.php/topic,152127.0.html), each dealing some splash damage. I am unsure as to how dealing damage works though. For example the following:
Code:
["fe_tome_of_fire", "Tome of Fire", [("book_a",0)], itp_type_thrown|itp_no_parry|itp_merchandise|itp_primary|itp_can_penetrate_shield|itp_bonus_against_shield|itp_crush_through|itp_ignore_friction, itcf_throw_axe, 420, weight(1)|difficulty(0)|spd_rtng(70)|shoot_speed(30)|thrust_damage(50,pierce)|max_ammo(35)|weapon_length(5), imodbit_cracked|imodbit_bent|imodbit_masterwork|imodbit_heavy|imodbit_strong,
[

#triggers for particle effects go here

(ti_on_missile_hit,[
(try_begin),
    (store_trigger_param_1, ":shooter"),

    (try_for_agents,":agent"),

		(ge, ":agent", 0),
		(agent_is_alive, ":agent"),
		(agent_get_position, pos2, ":agent"),
		(position_move_z, pos2, 50),
		(get_distance_between_positions, ":dist", pos1, pos2),
		
		# inner damage
		(try_begin),
			(le, ":dist", 100),
			(gt, ":dist", 0),	#don't hit primary target with splash
			(store_agent_hit_points, ":hp", ":agent", 1),
			(val_sub, ":hp", 20),
			(val_max, ":hp", 0),
			(agent_set_hit_points, ":agent", ":hp", 1),
			(try_begin),
				(eq, ":hp", 0),
				(agent_deliver_damage_to_agent, ":shooter", ":agent"),
			(try_end),
		(try_end),
		  
		  # outer damage
		(try_begin),
			(le, ":dist", 1000),
			(gt, ":dist", 100),
			(store_agent_hit_points, ":hp", ":agent", 1),
			(val_sub, ":hp", 20),
			(val_max, ":hp", 0),
			(agent_set_hit_points, ":agent", ":hp", 1),
			(try_begin),
				(eq, ":hp", 0),
				(agent_deliver_damage_to_agent, ":shooter", ":agent"),
			(try_end),
        (try_end),
    (try_end),
(try_end),
]),
]],

When tested in-game, it takes just over 2 hits to kill me, thrown at my feet. When armourless it says it deals around 47 damage, but armoured it can be anything from 3 to 11, while still taking the same number of hits to kill me. I *think* it's dealing true damage to me, but displaying what it has calculated I should take from the normal weapon damage (thrust_damage(50,pierce).

Is there a way to deal the proper amount of damage to an agent, depending on their armour? I tried (agent_set_hit_points, ":agent", ":hp",cut, 1) on the off chance, and somehow that made it take only around 1.5 hits to kill me regardless of armour, while still reporting the same damage as above.
 
"Damage" done via val_sub calculations and agent_set_hit_points in that code will ignore armor, since it directly sets the hp value.

Code:
agent_set_hit_points                     = 1721  # (agent_set_hit_points, <agent_id>, <value>,[absolute]),
                                                 # Sets new value for agent health. Optional last parameter determines whether the value is interpreted as actual health (absolute = 1) or relative percentile health (absolute = 0). Default is relative.

Use agent_deliver_damage_to_agent instead.

Code:
agent_deliver_damage_to_agent            = 1722  # (agent_deliver_damage_to_agent, <agent_id_deliverer>, <agent_id>, [damage_amount], [weapon_item_id]),
                                                 # Makes one agent deal damage to another. Parameter damage_amount is optional, if it is skipped or <= 0, then damage will be calculated using attacker's weapon item and stats (like a normal weapon attack). Optional parameter weapon_item_id was added in 1.153 and will force the game the calculate the damage using this weapon.
 
(ge, ":agent", 0), is really unnecessary in that agent loop
inner/outer damage seems identical, you really don't need another try_begin structure when you can do (is_between, ":distance", 100, 1001), instead. Your "primary target" might not actually get hit by the projectile if you miss - initialize 2 variables outside the agent loop (distance, agent #), keep a track of the closest agent (compare loop agent distance with the outer distance variable), and deal damage to the primary target after the loop. Also, for these "magic items" I'd recommend using itcf_throw_stone so it doesn't stick to shields (and spins around in mid-air), and itp_no_pick_up_from_ground so you can't reuse them if you miss.
 
Thanks for the help guys, it now looks like it's working fine. The damage for inner and outer should actually be different, that's a mistake in the code up there. It's also intentional that if if it is not a direct hit it does not deal full damage.

It now looks like this:
Code:
["fe_tome_of_fire", "Tome of Fire", [("book_a",0)], itp_type_thrown|itp_no_pick_up_from_ground|itp_merchandise|itp_primary|itp_can_penetrate_shield|itp_bonus_against_shield|itp_ignore_friction, itcf_throw_stone, 420, weight(1)|difficulty(0)|spd_rtng(70)|shoot_speed(30)|thrust_damage(50,pierce)|max_ammo(35)|weapon_length(20), imodbit_cracked|imodbit_bent|imodbit_masterwork|imodbit_heavy|imodbit_strong,
[
(ti_on_missile_hit,[
(try_begin),
    (store_trigger_param_1, ":caster"),
   (assign, ":inner_damage", 25),
   (assign, ":outer_damage", 10),
   
    (try_for_agents,":agent"),

      (agent_is_alive, ":agent"),
      (agent_get_position, pos2, ":agent"),
      (position_move_z, pos2, 50),
      (get_distance_between_positions, ":dist", pos1, pos2),
      
      # inner damage
      (try_begin), 
         (le, ":dist", 100),
         (gt, ":dist", 0),   #don't hit primary target with splash
         (agent_deliver_damage_to_agent, ":caster", ":agent", ":inner_damage"),
         
      # outer damage
      (else_try),
         (le, ":dist", 200),
         (gt, ":dist", 100),
         (agent_deliver_damage_to_agent, ":caster", ":agent", ":outer_damage"),
        (try_end),

    (try_end),
(try_end),
]),
]],

Bonus question: can I change/remove the mesh used once it is thrown? ie. I don't really want my mages to be throwing their books at people, as hilarious as it is to see them jam in people's skulls.
 
All vanilla arrows and bolts share the model "flying_missile" in flight:
Code:
["arrows","Arrows", [("arrow",0),("flying_missile",ixmesh_flying_ammo),("quiver", ixmesh_carry)], [...]
["khergit_arrows","Khergit Arrows", [("arrow_b",0),("flying_missile",ixmesh_flying_ammo),("quiver_b", ixmesh_carry)], [...]
["barbed_arrows","Barbed Arrows", [("barbed_arrow",0),("flying_missile",ixmesh_flying_ammo),("quiver_d", ixmesh_carry)], [...]
["bodkin_arrows","Bodkin Arrows", [("piercing_arrow",0),("flying_missile",ixmesh_flying_ammo),("quiver_c", ixmesh_carry)], [...]
["bolts","Bolts", [("bolt",0),("flying_missile",ixmesh_flying_ammo),("bolt_bag", ixmesh_carry),("bolt_bag_b", ixmesh_carry|imodbit_large_bag)], [...]
["steel_bolts","Steel Bolts", [("bolt",0),("flying_missile",ixmesh_flying_ammo),("bolt_bag_c", ixmesh_carry)], [...]
You could mayhap put an actual fireball in there, add the book as a quiver and have some kind of flame effect in hand.
 
I wanted to avoid the inevitable extra fiddling involved in creating a new "bow" with unique ammo, but if that's the only way...

Another part of what I am trying to do is actually make the fireball fire from above the agent's head. My current idea is shoot an invisible target painter-type projectile from the magic tome at 999 speed, then use the impact location to aim a fireball-type missile at that point, spawning above the caster's head. The only alternative I could find is using where the agent is looking directly, but that doesn't take accuracy or being mounted into account. I've scripted the following:

Code:
["fe_tome_of_fire2", "Tome of Fire MKII", [("book_a",0)], itp_type_thrown|itp_no_pick_up_from_ground|itp_merchandise|itp_primary|itp_can_penetrate_shield|itp_bonus_against_shield|itp_ignore_friction|itp_ignore_gravity, itcf_throw_stone, 420, weight(1)|difficulty(0)|spd_rtng(70)|shoot_speed(999)|thrust_damage(0,pierce)|max_ammo(35)|weapon_length(20), imodbit_cracked|imodbit_bent|imodbit_masterwork|imodbit_heavy|imodbit_strong,
[
(ti_on_missile_hit, [
        (display_message, "@HIT"),
	(store_trigger_param_1, ":caster"),
	(missile_remove_on_hit),

	#save location for fireball to aim at
	(agent_get_position, pos2, ":caster"),
	(store_trigger_param_1, ":caster"),
	(add_missile, ":caster", pos2, 30, "itm_fe_tome_of_fire2", 0, "itm_fe_fireball", 0),
]),

It doesn't work properly though. When fired, a loud buzzing noise is heard and the missile is nowhere to be seen. The same happens with any type of missile. I think it's constantly triggering ti_on_missile_hit, and my use of add_missile is probably off too...
 
This code did actually compile!? Your item declaration is unfinished, brackets are amiss:
Code:
[...]
	(add_missile, ":caster", pos2, 30, "itm_fe_tome_of_fire2", 0, "itm_fe_fireball", 0),
]) # trigger
]  # trigger-list
]  # item-tuple
,  # item-entry
If that be just a copy-paste error, I must admit to fail seeing the fault. No need to fetch caster twice, but that should not affect functionality.
Does it display the "HIT" message somewhen? Does it break the message system?

... I think you have to apply some math to pos2, though. It is supposed to encode both position and direction - and the height offset you wrote about has to be applied as well.

At this time, I cannot advise you about how a position is properly composed.
 
Ah yes, that was a copy/paste error. Please find it in your heart to forgive me.

It does display "HIT," repeatedly until they scroll off screen. I took this as an infinite call to the contents of ti_on_missile_hit breaking the message system.

I tried the following instead as a test:
Code:
(ti_on_weapon_attack, [
	(store_trigger_param_1, reg1),
	(copy_position, pos2, pos1),		#pos1 = caster pos
	#(position_move_z, pos2, 200, 0),	#pos2 = fireball spawn pos - moves up 2 metres
	(add_missile, reg1, pos2, 3000, "itm_fe_tome_of_fire2", 0, "itm_fe_fireball", 0),
]),

This version does spawn a fireball, but that for some reason pings off slightly to the left and downwards. I think you're right Zsar, something's up with pos2 - it needs a vector of motion, somehow. Also, it only fires at anything approaching the correct speed if I multiply it by 100, and using position_move_z makes the missile disappear.

Nnngh.
 
Nah, "breaking" it would mean to see no messages at all, not even those that normally should spawn.
Hohum, when using "ti_on_weapon_attack", how would one then fetch the position of impact?

Alright, I did some reading:
A position is at least a 6-tuple, containing the following values:
(x0, x1, x2) - denoting a point in the game world; can be interpreted as relative or absolute
(r0, r1, r2) - denoting the facing, a rotation around the dimensional axes

For the purpose of flight direction, the first three values would denote the origin, the next three ones the direction.
... Damnation, I know this is established conduct, but why can engine builders not use vectors for everything!? I hate angles!

So, pos2 has to face pos1 after being shifted to the appropriate height. Did you try to employ one of these variants:
Code:
(position_move_z, pos2, <delta>, 0), # using relative interpretation; should be axis from feet to head
(position_move_z, pos2, <delta>, 1), # using absolute interpretation; should be straight up
Another thing to do is to invoke "set_fixed_point_multiplier" with a value of 10*c, say 100, at the very beginning. That should help to interprete things.

There is a function to fetch the "angle" between two points but no hint as to what this angle be. As there is not a function to set an angle, it may be in fact the missing 3-tuple of (r0, r1, r2). Then this would work:
Code:
(get_angle_between_positions, ":angle", pos2, pos1), # ideally fetched the 3-tuple (r0, r1, r2)
(position_rotate_x, pos2, ":angle"),                 # ideally fetched only r0
(position_rotate_y, pos2, ":angle"),                 # ideally fetched only r1
(position_rotate_z, pos2, ":angle"),                 # ideally fetched only r2
I fear it be not as easy, but the results might give a hint as to how to proceed correctly.
- Mayhap the movement part should be solved first, though.

If the direction diverges by a relatively small amount, to employ "position_rotate_[x|y|z]_floating" instead may provide correction.
It might be that the angle has to be manually upscaled in that case by "(convert_to_fixed_point, ":angle")".
 
Alright, here's where I'm up to:

Code:
(ti_on_missile_hit, [
	(display_message, "@HIT"),
	(store_trigger_param_1, reg1),		#casting agent
	(agent_get_position, pos2, reg1),
	(set_fixed_point_multiplier, 100),
	#pos1 = target, pos2 = spawn point
	
	(get_angle_between_positions, ":zangle", pos2, pos1),	#z axis only
	(position_rotate_z, pos2, ":zangle"),

	#(position_copy_rotation, pos2, pos1),	#copies rotation of target to new missile (ROUGH)
	(position_move_z, pos2, 300, 1),		#moves up 3 metres
	
	(add_missile, reg1, pos2, 3000, "itm_fe_tome_of_fire2", 0, "itm_fe_fireball", 0),
]),

Thanks for the get_angle_between_positions tip-off. I read that get_angle_between_positions only uses the z-axis, so that's all I've used here. I might be in for some trigonometry to work out the angle on every axis.

The fireball now spawns at the correct height, but still causes multiple triggers of ti_on_missile_hit - when a fireball hits, it launches another from wherever I am. It may be to do with the call to create a fireball being nested inside a ti_on_missile_hit trigger, at a guess. The frequency of multiple hits also increases with the speed of the tome too for some reason.

Oh, and it launches the fireballs off at varying angles around the z-axis in front of me, but I dare to hope that that may be easy to solve compared to the rest of the problems.

This module system has given me a new-found appreciation for good code documentation.

PS. what does set_fixed_point_multiplier actually do..?
 
... Mayhap... if you rotated both source and target in a way that the angle around the z-axis denoted the angle around the x-axis after reversing this rotation...

I am horribly out of practice with angular calculations, but I have a feeling that this may be feasible. Rotating by Pi/2 around - global - x would turn the local z-axis parallel to the former y-axis, if I am not mistaken. Fetching an angle then, rotating back and applying this angle to the y-axis should be proper.
Thereafter rinse and repeat with x and y swapped.

... I am reasonably sure I recall this or a similar practice from my computer graphics lecture.

If all else fails, there are functions available to go the hard way:
Code:
store_sqrt = 2125  # (store_sqrt, <destination_fixed_point>, <value_fixed_point>), takes square root of the value
store_pow  = 2126  # (store_pow, <destination_fixed_point>, <value_fixed_point>, <value_fixed_point), takes square root of the value
                   #  dest, op1, op2 :      dest = op1 ^ op2
store_sin  = 2127  # (store_sin, <destination_fixed_point>, <value_fixed_point>), takes sine of the value that is in degrees
store_cos  = 2128  # (store_cos, <destination_fixed_point>, <value_fixed_point>), takes cosine of the value that is in degrees
store_tan  = 2129  # (store_tan, <destination_fixed_point>, <value_fixed_point>), takes tangent of the value that is in degrees
Koozer said:
what does set_fixed_point_multiplier actually do..?
Outside of trigger intervals, the MS only handles integral numbers. Fixed point is a way to denote a real number with fixed precision by assigning to one position within the integral number the decimal point. Hence "fixed point".
In practice, it works like this:

One wished a real number with three decimal places. Therefore one took this integral number and multiplied it with 1000, memorising this as the neutrality factor F...
To multiply two integral numbers A and B, one then calculated: A * B. No change.
To divide integral numbers A through B, one calculated: (A * F) / B and gained the fixed point number C, whose first three positions were behind the decimal point.
To multiply fixed point numbers A and B, one calculated (A * B) / F for a fixed point result and (A * B) / pow(F, 2) for an integral result.
To divide fixed point numbers A and B, one treated them as integral numbers, (A * F) / B again, and might choose to apply correct rounding before dividing through F once more to return to integral numbers again.

"set_fixed_point_multiplier" sets F for use in internal calculations. For your own calculations, employ "convert_to_fixed_point" and "convert_from_fixed_point" - those will grab the F set there, so you need not care about changing factors.
Koozer said:
This module system has given me a new-found appreciation for good code documentation.
Hell, yes! One should make M&B-modding a mandatory lesson for Software Engineers.
 
So I did it the long way:

Code:
(ti_on_missile_hit, [
	(display_message, "@HIT"),
	(set_fixed_point_multiplier, 100),
	(store_trigger_param_1, reg1),		#casting agent
	(agent_get_position, pos2, reg1),
	(position_move_z, pos2, 300, 1),	#moves up 2 metres
	
	#pos1 = target, pos2 = fireball spawn point
	(position_get_x, ":pos1x", pos1),	#pos1 x
	(position_get_y, ":pos1y", pos1),	#pos1 y
	(position_get_z, ":pos2z", pos2),	#pos2 z
	
	(init_position, pos3),
	(position_set_x, pos3, ":pos1x"),	#same position on x-y plane as pos1
	(position_set_y, pos3, ":pos1y"),
	(position_set_z, pos3, ":pos2z"),	#same height as pos2
	
	(get_distance_between_positions, ":dist23", pos2, pos3),	#opposite
	(get_distance_between_positions, ":dist13", pos1, pos3),	#adjacent
	(val_div, ":dist13", ":dist23"),							#o/a
	(store_atan, ":xangle", ":dist13"),							#arctan(o/a)
	
	(get_angle_between_positions, ":zangle", pos2, pos1),	#z rotation
	
	(position_rotate_x, pos2, ":xangle", 1),	#FINALLY apply new rotations
	(position_rotate_z, pos2, ":zangle", 1),
	
	(add_missile, reg1, pos2, 3000, "itm_fe_tome_of_fire2", 0, "itm_fe_fireball", 0),
]),

Still registers multiple hits and it still pings off at a random z rotation. X rotation is a little high too. It is eminently possible that my maths is off here, but I can't explain why the z rotation would change every time I chuck a spell at the same spot without moving. I commented out add_missile again and it only registers one hit as it should.

I think for now it would be best to find a way around using ti_on_missile_hit the way I am doing. I found store_proficiency_level, so maybe I can use that along with agent_get_look_position...somehow...

To Google!
 
Say... could I borrow you to solve a minor problem in my bachelor thesis?
It involves discerning the position of a point relative to a plane on the basis of its normal vector in an n-dimensional space.
 
Do not be fooled by my rudimentary grasp on trigonometry, I am wholly confident there are better people than I to help on this board alone. If you want to PM me any details I can have a stab at the problem, but be warned!
 
Example of a weapon spawning multiple projectiles on attack.

Code:
["flintlock_pistol","Flintlock (bullets)",[("flintlock_pistol",0)],itp_type_crossbow|itp_merchandise|itp_primary|itp_bonus_against_shield,itcf_shoot_pistol|itcf_reload_pistol|itcf_carry_revolver_right,5000,weight(1.5)|spd_rtng(80)|shoot_speed(65)|thrust_damage(80,pierce)|max_ammo(1)|accuracy(95),imodbits_none,[(ti_on_weapon_attack,[
(store_trigger_param_1,reg1),#Get the attacker Agent for add_missile
(try_for_range, ":unused", 1, 3),
	(copy_position, pos2, pos1),
	
	#Shotgun script
	#Pistol Version

	#Kludge code to fix the final angles and arrive at a satisfactory result.
	#The engine doesn't appear to use Euler angles to give perfect precision...
	#Under all circumstances, so this will be off a bit if firing way up or down.
	(set_fixed_point_multiplier, 100),
	(assign, ":z_fix", -4850),#-48.5 degrees
	#The following line is our "choke" adjustment on the Z axis.
	#Unless you need a fan pattern, You'll want to keep the change...
	#The same for X and Z, but Z needs to be applied through the special fix below.	
	(store_random_in_range, ":z_offset", -150, 150),#Random Rotation of Z
	(val_add, ":z_fix", ":z_offset"),#Add offset to the Z fix
	#Fix for Z axis, using floating-point adjustment of the quaternian
	#Don't mess with the next three lines...
	#Unless you really know what's going on with the math :-)	
	(position_rotate_x_floating, pos2, 9000), # change axis by rotating 90 degrees
	(position_rotate_y_floating, pos2, ":z_fix"), # rotate z floating
	(position_rotate_x_floating, pos2, -9000), # change axis back
	#Choke adjustment for the X axis.  
	#Should match the choke adjustment for the Z in most cases.	
	(store_random_in_range, ":x_offset", -150, 151),#Random Rotation of X
	(position_rotate_x_floating, pos2, ":x_offset"), # Final adjustment of X
	
	(add_missile, reg1, pos2, 6500, "itm_flintlock_pistol", 0, "itm_shotgun_pellets", 0),
(try_end),
(play_sound,"snd_pistol_shot"),#Firing sound
#(position_move_x,pos1,50),#Move along X
(position_move_y,pos1,50),#Move out along Y
(particle_system_burst,"psys_pistol_smoke",pos1,15),#Smoke
(particle_system_burst,"psys_gun_sparks",pos1,15),#Flare
]),]],

Example of a projectile spawning multiple projectiles that can cause damage on a hit:

Code:
["xenoargh_troop_grenade","Hand Grenades",[("xeno_bullet",0),("xeno_cannonball_explosive",ixmesh_flying_ammo),("xeno_cannonball_explosive",ixmesh_inventory)],itp_type_thrown|itp_no_pick_up_from_ground|itp_primary,itcf_throw_stone,4500,weight(5)|spd_rtng(70)|shoot_speed(70)|thrust_damage(50,pierce)|max_ammo(2)|weapon_length(100)|accuracy(75),imodbits_none,
[
(ti_on_missile_hit,
      [
			(copy_position, pos63, pos1),#Need to store it in a safe-ish place here.
			(position_get_distance_to_ground_level, ":distance", pos63),
			(try_begin),
				(lt, ":distance", 0),
				(position_set_z_to_ground_level, pos63),
				(position_move_z, pos63, 200),
			(try_end),				
			(this_or_next|multiplayer_is_dedicated_server),		  
			(this_or_next|multiplayer_is_server),
			(neg|game_in_multiplayer_mode),	
			(store_trigger_param_1,reg1),
			(try_for_range, ":unused", 1, 17),
				(copy_position, pos2, pos63),
				(store_random_in_range, ":x_offset", 1, 361),#Random Rotation of X
				(position_rotate_x, pos2, ":x_offset"),	
				(store_random_in_range, ":y_offset", 1, 361),#Random Rotation of Y
				(position_rotate_y, pos2, ":y_offset"),	
				(store_random_in_range, ":z_offset", 1, 361),#Random Rotation of Z
				(position_rotate_z, pos2, ":z_offset"),					
				(add_missile, reg1, pos2, 1500, "itm_sniper_crossbow", 0, "itm_grenade_fragments", 0),
			(try_end),	
			(particle_system_burst,"psys_explosion_dirt",pos63,200),			
			(particle_system_burst,"psys_explosive_explosion_sparks_a",pos63,35),
			(particle_system_burst,"psys_explosive_explosion_sparks_b",pos63,35),
			(play_sound_at_position, "snd_explosive_ammo_explode", pos63),			  
]),
]],

Example of a projectile that uses a spherical area-search to determine hits within an AOE with some fancy stuff.

Code:
#DEPRECATED; LEFT HERE AS REFERENCE ONLY FOR OLD-STYLE AOE CODE.
#This code works, but the newer style, while computationally more expensive, is cooler.   
["shotshell_cannon_rounds","Shot-Shell Cannon Round",[("xeno_bullet",0),("xeno_cannonball_shotshell",ixmesh_flying_ammo),("bolt_bag_c",ixmesh_carry),("xeno_cannonball_shotshell",ixmesh_inventory)],itp_type_bullets|itp_can_penetrate_shield|itp_can_knock_down|itp_no_pick_up_from_ground|itp_default_ammo,itcf_carry_quiver_right_vertical,2500,weight(3)|abundance(0)|weapon_length(150)|thrust_damage(15,pierce)|max_ammo(30),imodbits_none,
   [
  (ti_on_missile_hit,
      [
          #pos1 - Missile hit position
         #param_1 - Shooter agent

          (try_begin),
			(this_or_next|multiplayer_is_dedicated_server),
			(this_or_next|multiplayer_is_server),
			(neg|game_in_multiplayer_mode),
			(init_position,pos63),
			(init_position,pos62),			 
			 
             (store_trigger_param_1,":shooter"),
			 (agent_get_team, ":shooter_team", ":shooter"),	
			 (copy_position, pos63, pos1),			 
			 (agent_get_position,pos62,":shooter"),
			 (get_distance_between_positions,":shoot_dist",pos1,pos62),
			(try_begin),
				#(lt,":shoot_dist",4000),#40 meter effective range,pretty weak vs. armor.
				(ge,":shoot_dist",2000),
				(assign,":shot_spread",450),#Wider spread.
				(store_random_in_range,":random_no",10,40),#larger range, to deal with heavy armors
				#(assign,reg0,":random_no"),
				#(display_message,"@Shot does {reg0} damage.",0xFFFFAAAA),
			(else_try),
				(lt,":shoot_dist",2000),#This is medium killing range,still deadly vs. against low-armor targets,but usually 2 or more shots are required.
				(ge,":shoot_dist",1000),
				(assign,":shot_spread",250),#Narrow spread.
				(store_random_in_range,":random_no",15,90),#larger range, to deal with heavy armors
				#(assign,reg0,":random_no"),
				#(display_message,"@Shot does {reg0} damage.",0xFFFFAAAA),				
			(else_try),
				(lt,":shoot_dist",1000),#At < 10 meters,it's pretty much insta-death for anything it hits.
				(assign,":shot_spread",150),#Narrow spread.
				(store_random_in_range,":random_no",200,300),#larger range, to deal with heavy armors
				#(assign,reg0,":random_no"),
				#(display_message,"@Shot does {reg0} damage.",0xFFFFAAAA),
			(try_end),
			(position_set_y, pos63, 0),
			(try_begin),
			 (lt,":shoot_dist",4000),
             (try_for_agents,":agent"),
				(agent_get_team, ":agent_team", ":agent"),
				(teams_are_enemies, ":agent_team", ":shooter_team"),
				(agent_is_active,":agent"),
				(agent_is_alive,":agent"),	
				(init_position,pos61),
                (agent_get_position,pos61,":agent"),
				(position_set_y, pos61, 0),
                (get_distance_between_positions,":dist",pos63,pos61),
                (try_begin),
				   (lt,":dist",":shot_spread"),
				   (position_move_z,pos61,125),
				   (position_move_y,pos61,15),
				   (particle_system_burst,"psys_blood_small",pos61,35),
					(try_for_range,":equip_type",itm_leather_armor,itm_black_armor),
						(try_begin),
							(agent_has_item_equipped,":agent",":equip_type"),
							(val_sub,":random_no",10),#Armor offers more protection than against explosive shell.
						(try_end),
					(try_end),
					(agent_deliver_damage_to_agent,":shooter",":agent",":random_no"),
                (try_end),
             (try_end),
          (try_end),
]),
]],

For a "wand" in a fantasy situation, suggest re-purposing the pistol, it's almost ideal.  You just have to develop the custom assets. 

The bigger problem you'll hit, once you want to get beyond just a fireball, is dealing with ammunition in this engine, where ammo is assigned per-class, but cannot be assigned to a specific weapon only (they really didn't ever see the need for multiple ammo types that are exclusive, apparently). 

To get around that, I'd suggest downloading Blood and Steel (it's the only way to get the source), playing it for a bit to see how it works, then perusing the ammunition system source, which is all documented.
 
Dear god...looking over that grenade script, I noticed they used "itm_sniper_crossbow" as the missile creator.

So I tried it out, and lo and behold my fireballs now don't start firing in an infinite stream! So the problem was that referencing the tome in add_missile caused the ti_on_missile_hit script to keep running and getting stuck in an infinite loop. Thanks Xenoargh! Why the hell is that item field in add_missile? What does it even noticeably do besides ruin my day?

Still fires fireballs off at random z rotations, but one step at a time...
 
Ah, I think I understand. When "add_missile" is invoked, it actually does the very same as using the item in question would do. Therefore, a hit with "itm_fe_tome_of_fire2" triggers a shot of "itm_fe_tome_of_fire2", which will hit something, which triggers a shot of "itm_fe_tome_of_fire2", which will hit something... well, and there we have that.

... This is a positively funky way to create a while-loop. The Module System definitely qualifies as an esoteric programming language by now.
Who needs INTERCAL and its "COME FROM" when we can have the "DO NOT STOP SHOOTING YET"-loop!
 
Yeah, beware infinite loops; you can even crash the engine by not creating breaks.

Why the hell is that item field in add_missile? What does it even noticeably do besides ruin my day?
Well, I suspect it has something to do with infinite-loop problems.  They tacked this onto the engine after it'd been available via WSE for quite some time; it's not like the engine was developed to handle this sort of stuff per se.  Like a lot of the other fancy stuff, it's based on code that people developed after the engine guys gave us new toys to play with.

That, and note that the projectile wants a valid Agent ID, to credit the shot to the right Agent.  It's important to make sure that Agent's valid, FYI.

Also note that the projectile type flag determines what Skill gets any rewards for a successful attack. 

I'm having to fix that in places; I've got Agents who are getting high scores for their Crossbow skills when they throw grenades :lol:
 
Back
Top Bottom