blob: 55784834132b3c152b036b8683f8dc2244d8a2b7 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// ranged_weapon.c -- Standardobjekt fuer Fernkampfwaffen
4//
5// $Id: ranged_weapon.c 8820 2014-05-14 19:31:46Z Zesstra $
6
7// Gebraucht wird Munition mit einer MUN_* id. Dieses Objekt sollte ausserdem
8// P_DAM_TYPE setzen sowie P_SHOOTING_WC und ggf. P_HIT_FUNC. In der Regel
9// sollten diese Objekte units sein, da groessere Anzahlen davon gebraucht
10// werden.
11#pragma strong_types
12#pragma save_types
13#pragma no_clone
14#pragma pedantic
15#pragma range_check
16
17inherit "/std/living/skill_utils";
18inherit "/std/weapon";
19
20#include <ranged_weapon.h>
21
22#include <properties.h>
23#include <defines.h>
24#include <combat.h>
25#include <new_skills.h>
26#include <unit.h>
27
28static int last_shoot;
29
30protected void create()
31{
32 ::create();
33
34 SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
35 SetProp(P_STRETCH_TIME, 1);
36 SetProp(P_AMMUNITION, MUN_ARROW);
37 SetProp(P_SHOOTING_WC, 70);
38 SetProp(P_RANGE, 50);
39 SetProp(P_INFO, "Syntax: schiesse WAS auf WEN?\n");
40 // fuer den Fall, das man den Bogen als Knueppel nutzt...
41 SetProp(P_WC, 30);
42 SetProp(P_QUALITY, 100);
43
44 AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
45}
46
47static int shoot_dam(mapping shoot)
48// ausgegliedert, damit man diese Funktion ggf. ueberschreiben kann.
49{ mixed res;
50 object hitfunc;
51 closure usk;
52
53 // diesselbe Formel wie bei Nahkampfwaffen, nur das hier DEX anstatt STR
54 // benutzt wird, und die WC der Munition noch addiert wird
55 shoot[SI_SKILLDAMAGE] = random(1+( 2*(shoot[P_WC]+shoot[P_SHOOTING_WC])
56 + 10*PL->QueryAttribute(A_DEX) )/3);
57 // HitFunc der Munition addieren
58 if ( objectp(hitfunc=(shoot[P_AMMUNITION]->QueryProp(P_HIT_FUNC))) )
59 shoot[SI_SKILLDAMAGE] += (hitfunc->HitFunc(shoot[SI_ENEMY]));
60
61 usk=symbol_function("UseSkill",PL);
62
63 // Schuss-Skill der Gilde fuer diesen Munitionstyp anwenden
64 if ( mappingp(res=funcall(usk,SHOOT(shoot[P_WEAPON_TYPE]),
65 deep_copy(shoot))) )
66 SkillResTransfer(res, &shoot);
67
68 // Allgemeinen Schuss-Skill der Gilde anwenden
69 if ( mappingp(res=funcall(usk,SK_SHOOT,deep_copy(shoot))) )
70 SkillResTransfer(res, &shoot);
71
72 return shoot[SI_SKILLDAMAGE];
73}
74
75static string FindRangedTarget(string str, mapping shoot)
76{ string wen;
77 mixed area;
78 int psr;
79
80 if ( sscanf(str, "%s auf %s", str, wen)!=2 )
81 {
82 shoot[SI_ENEMY]=PL->SelectFarEnemy(PL->PresentEnemies(),1,4);
83 str = shoot[P_WEAPON_TYPE];
84 }
85 else if ( objectp(shoot[SI_ENEMY]=present(wen, environment(PL))) )
86 {
87 return str;
88 }
89 else if ( ( (area=(environment(PL)->QueryProp(P_TARGET_AREA)))
90 || (area=environment(environment(PL))) )
91 && ( objectp(area)
92 || ( stringp(area)
93 && objectp(area=find_object(area)) ) )
94 && (psr=environment(PL)->QueryProp(P_SHOOTING_AREA)) )
95 {
96 shoot[SI_ENEMY]=present(wen, area);
97 if ( shoot[SI_ENEMY] && (psr>QueryProp(P_RANGE)) )
98 {
99 write(break_string((shoot[SI_ENEMY]->Name(WER))+
100 " ist leider ausserhalb der Reichweite "+name(WESSEN, 1)+".",
101 78));
102 return 0;
103 }
104 }
105
106 if ( !shoot[SI_ENEMY] )
107 {
108 write("Es ist kein Feind vorhanden auf den Du schiessen koenntest.\n");
109 return 0;
110 }
111
112 return str;
113}
114
115static int cmd_shoot(string str)
116{ int dam;
117 string no_at;
118 object quiver;
119 mapping shoot;
120
121 if ( PL->QueryProp(P_GHOST) )
122 {
123 write("Deine immateriellen Finger gleiten durch die Waffe hindurch.\n");
124 return 1;
125 }
126
127 if ( !QueryProp(P_NOGET) )
128 {
129 // Katapulte oder aehnliches die von Magiern aufgestellt werden
130 // muessen natuerlich nicht gezueckt werden :).
131 if ( !QueryProp(P_WIELDED) )
132 {
133 notify_fail(break_string("Du solltest "+name(WEN, 1)+
134 " dazu schon zuecken.", 78));
135 return 0;
136 }
137
138 if ( (PL->InFight()) && ((PL->PresentPosition())<2) )
139 {
140 write(break_string(
141 "Du solltest Dich erst etwas weiter zurueckziehen, um mit "+
142 name(WEM, 1)+" schiessen zu koennen!", 78));
143 return 1;
144 }
145 }
146
147 if ( ( (QueryProp(P_EQUIP_TIME)+QueryProp(P_STRETCH_TIME)*2) > time() )
148 || ( (last_shoot+QueryProp(P_STRETCH_TIME)) > absolute_hb_count() ) )
149 {
150 write(break_string("Du kannst mit "+name(WEM, 1)+
151 " noch nicht wieder schiessen.", 78));
152 return 1;
153 }
154
155 if ( (PL->QueryProp(P_DISABLE_ATTACK))>0 )
156 {
157 write(break_string("Solange Du noch gelaehmt bist, kannst Du "+
158 name(WEN, 1)+" nicht benutzen!", 78));
159 return 1;
160 }
161
162 if ( PL->QueryProp(P_ATTACK_BUSY) )
163 {
164 write("Nicht so hektisch! So schnell bist Du nicht.\n");
165 return 1;
166 }
167
168 // P_ATTACK_BUSY natuerlich auch setzen...
169 PL->SetProp(P_ATTACK_BUSY,1);
170
171 // Info-Mapping erzeugen
172 shoot = ([ P_WEAPON : ME,
173 P_WEAPON_TYPE : QueryProp(P_AMMUNITION),
174 P_STRETCH_TIME : QueryProp(P_STRETCH_TIME),
175 P_WC : QueryProp(P_SHOOTING_WC),
176 SI_SPELL : 0
177 ]);
178
179 if ( !stringp(str) || str=="" )
180 str=shoot[P_WEAPON_TYPE];
181 else if (str[0..3]=="mit ")
182 str=str[4..];
183 else if (str[0..3]=="auf ")
184 str=shoot[P_WEAPON_TYPE]+" "+str;
185
186 if ( !(str=FindRangedTarget(str,shoot)) )
187 return 1;
188
189 if ( shoot[SI_ENEMY]->QueryProp(P_GHOST) )
190 {
191 write(break_string("Aber "+(shoot[SI_ENEMY]->name(WER, 1))+
192 " ist doch ein Geist!", 78));
193 return 1;
194 }
195 else if ( no_at=(shoot[SI_ENEMY]->QueryProp(P_NO_ATTACK)) )
196 {
197 if ( stringp(no_at) )
198 write(no_at);
199 else
200 write(break_string("Du kannst "+(shoot[SI_ENEMY]->name(WEN, 1))+
201 " nicht angreifen.", 78));
202 return 1;
203 }
204 else if ( shoot[SI_ENEMY]==PL )
205 {
206 write("Du kannst doch nicht auf Dich selbst schiessen!\n");
207 return 1;
208 }
209
210 if ( !(shoot[P_AMMUNITION]=present(str, PL))
211 && ( !objectp(quiver=PL->QueryArmourByType(AT_QUIVER))
212 || !(shoot[P_AMMUNITION]=present(str, quiver)) ) )
213 {
214 if ( str==shoot[P_WEAPON_TYPE] )
215 write(break_string("Du hast keine passende Munition bei Dir, um "+
216 "mit "+name(WEM, 1)+" schiessen zu koennen.",
217 78));
218 else
219 write(break_string("Du hast kein '"+str+"' bei Dir.", 78));
220 return 1;
221 }
222
223 if ( !shoot[P_AMMUNITION]->id(shoot[P_WEAPON_TYPE]) )
224 {
225 write(break_string((shoot[P_AMMUNITION]->Name(WER))+" kannst Du mit "+
226 name(WEM, 1)+" nicht verschiessen.", 78));
227 return 1;
228 }
229
230 shoot[P_AMMUNITION]->SetProp(U_REQ, 1);
231
232 shoot[SI_SKILLDAMAGE_MSG] = shoot[SI_SKILLDAMAGE_MSG2]
233 = shoot[P_AMMUNITION]->name(WEN);
234 shoot[SI_SKILLDAMAGE_TYPE] = ( shoot[P_AMMUNITION]->QueryProp(P_DAM_TYPE)
235 || ({DT_PIERCE}) );
236
237 shoot[P_SHOOTING_WC] = shoot[P_AMMUNITION]->QueryProp(P_SHOOTING_WC);
238
239 dam=shoot_dam(shoot);
240
241 // Textausgabe
242 if (shoot[SI_ENEMY])
243 {
244 tell_object(PL,break_string(
245 "Du schiesst "+shoot[SI_SKILLDAMAGE_MSG]+" auf "+
246 shoot[SI_ENEMY]->name(WEN, 1)+".", 78) );
247 tell_room(environment(PL), break_string(
248 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
249 shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
250 ({ shoot[SI_ENEMY], PL }) );
251 if ( environment(PL)!=environment(shoot[SI_ENEMY]) )
252 tell_room(environment(shoot[SI_ENEMY]),break_string(
253 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
254 shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
255 ({ shoot[SI_ENEMY] }) );
256 tell_object(shoot[SI_ENEMY], break_string(
257 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+
258 " auf Dich ab.",78) );
259
260 shoot[SI_ENEMY]->Defend(dam,shoot[SI_SKILLDAMAGE_TYPE],shoot[SI_SPELL],PL);
261 }
262 // Munition verbrauchen
263 if ( shoot[P_AMMUNITION]->IsUnit() )
264 shoot[P_AMMUNITION]->AddAmount(-1);
265 else
266 shoot[P_AMMUNITION]->remove();
267
268 // es wird an dieser stelle absolute_hb_count anstelle von time() benutzt
269 // um dem Spieler wirklich die vollen 2 sek Zeit zu geben um zu reagieren
270 // ohne das er dadurch einen Schaden hat (wie er es z.b. bei time() haette)
271 last_shoot=absolute_hb_count();
272
273 return 1;
274}
275